6.19.4-rc1 review
Greg Kroah-Hartman
maintainerPATCH 370/781 PCI: Remove old_size limit from bridge window sizing
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit f909e3ee3ed1a44202f09ac7e637a0f9ec372225 ]
calculate_memsize() applies lower bound to the resource size before aligning the resource size making it impossible to shrink bridge window resources. I've not found any justification for this lower bound and nothing indicated it was to work around some HW issue.
Prior to the commit 3baeae36039a ("PCI: Use pci_release_resource() instead of release_resource()"), releasing a bridge window during BAR resize resulted in clearing start and end address of the resource. Clearing addresses destroys the resource size as a side-effect, therefore nullifying the effect of the old size lower bound.
After the commit 3baeae36039a ("PCI: Use pci_release_resource() instead of release_resource()"), BAR resize uses the aligned old size, which results in exceeding what fits into the parent window in some cases:
xe 0030:03:00.0: [drm] Attempting to resize bar from 256MiB -> 16384MiB
xe 0030:03:00.0: BAR 0 [mem 0x620c000000000-0x620c000ffffff 64bit]: releasing
xe 0030:03:00.0: BAR 2 [mem 0x6200000000000-0x620000fffffff 64bit pref]: releasing
pci 0030:02:01.0: bridge window [mem 0x6200000000000-0x620001fffffff 64bit pref]: releasing
pci 0030:01:00.0: bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref]: releasing
pci 0030:00:00.0: bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref]: was not released (still contains assigned resources)
pci 0030:00:00.0: Assigned bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref] to [bus 01-04] free space at [mem 0x6200400000000-0x62007ffffffff 64bit pref] pci 0030:00:00.0: Assigned bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref] to [bus 01-04] cannot fit 0x4000000000 required for 0030:01:00.0 bridging to [bus 02-04]
The old size of 0x6200000000000-0x6203fbff0ffff resource was used as the lower bound which results in 0x4000000000 size request due to alignment. That exceeds what can fit into the parent window.
Since the lower bound never even was enforced fully because the resource addresses were cleared when the bridge window is released, remove the old_size lower bound entirely and trust the calculated bridge window size is enough.
This same problem may occur on io window side but seems less likely to cause issues due to general difference in alignment. Removing the lower bound may have other unforeseen consequences in case of io window so it's better to leave it as -next material if no problem is reported related to io window sizing (BAR resize shouldn't touch io windows anyway).
--- drivers/pci/setup-bus.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
drivers/pci/setup-bus.c +3 -8
@@ -1070,16 +1070,13 @@ static resource_size_t calculate_memsize(resource_size_t size,resource_size_t min_size, resource_size_t add_size, resource_size_t children_add_size, - resource_size_t old_size, resource_size_t align) { if (size < min_size) size = min_size; -if (old_size ==1 ) - old_size =0 ; size =max (size, add_size) + children_add_size; -return ALIGN (max (size, old_size), align); +return ALIGN (size, align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
@@ -1297,7 +1294,6 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,resource_size_t children_add_size =0 ;resource_size_t children_add_align =0 ;resource_size_t add_align =0 ; -resource_size_t old_size;if (!b_res)return ;
@@ -1363,11 +1359,10 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, } } - old_size =resource_size (b_res); win_align =window_alignment (bus, b_res->flags); min_align =calculate_head_align (aligns, max_order); min_align =max (min_align, win_align); - size0 =calculate_memsize (size, min_size,0 ,0 , old_size, win_align); + size0 =calculate_memsize (size, min_size,0 ,0 , win_align);if (size0) {resource_set_range (b_res, min_align, size0);
@@ -1377,7 +1372,7 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,if (realloc_head && (add_size >0 || children_add_size >0 )) { add_align =max (min_align, add_align); size1 =calculate_memsize (size, min_size, add_size, children_add_size, - old_size, win_align); + win_align); }if (!size0 && !size1) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 371/781 tcp: tcp_tx_timestamp() must look at the rtx queue
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 838eb9687691d29915797a885b861fd09353386e ]
tcp_tx_timestamp() is only called at the end of tcp_sendmsg_locked() before the final tcp_push().
By the time it is called, it is possible all the copied data has been sent already (transmit queue is empty).
If this is the case, use the last skb in the rtx queue.
--- net/ipv4/tcp.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv4/tcp.c +3 -0
@@ -501,6 +501,9 @@ static void tcp_tx_timestamp(struct sock *sk, struct sockcm_cookie *sockc)struct sk_buff *skb =tcp_write_queue_tail (sk);u32 tsflags = sockc->tsflags; +if (unlikely (!skb)) + skb =skb_rb_last (&sk->tcp_rtx_queue); +if (tsflags && skb) {struct skb_shared_info *shinfo =skb_shinfo (skb);struct tcp_skb_cb *tcb =TCP_SKB_CB (skb);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 372/781 Bluetooth: hci_conn: Fix using conn->le_{tx,rx}_phy as supported PHYs
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 129d1ef3c5e60d51678e6359beaba85771a49e46 ]
conn->le_{tx,rx}_phy is not actually a bitfield as it set by HCI_EV_LE_PHY_UPDATE_COMPLETE it is actually correspond to the current PHY in use not what is supported by the controller, so this introduces different fields (conn->le_{tx,rx}_def_phys) to track what PHYs are supported by the connection.
--- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 17 +++++++++++------ net/bluetooth/hci_event.c | 30 +++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 9 deletions(-)
include/net/bluetooth/hci_core.h +2 -0
@@ -730,6 +730,8 @@ struct hci_conn {__u16 le_per_adv_data_offset;__u8 le_adv_phy;__u8 le_adv_sec_phy; +__u8 le_tx_def_phys; +__u8 le_rx_def_phys;__u8 le_tx_phy;__u8 le_rx_phy;__s8 rssi;
net/bluetooth/hci_conn.c +11 -6
@@ -1008,6 +1008,11 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,/* conn->src should reflect the local identity address */ hci_copy_identity_address (hdev, &conn->src, &conn->src_type); conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; +/* Use the controller supported PHYS as default until the +* remote features are resolved. +*/ + conn->le_tx_def_phys = hdev->le_tx_def_phys; + conn->le_rx_def_phys = hdev->le_tx_def_phys;break ;case CIS_LINK :/* conn->src should reflect the local identity address */
@@ -2928,22 +2933,22 @@ u32 hci_conn_get_phy(struct hci_conn *conn)break ;case LE_LINK : -if (conn->le_tx_phy &HCI_LE_SET_PHY_1M ) +if (conn->le_tx_def_phys &HCI_LE_SET_PHY_1M ) phys |=BT_PHY_LE_1M_TX ; -if (conn->le_rx_phy &HCI_LE_SET_PHY_1M ) +if (conn->le_rx_def_phys &HCI_LE_SET_PHY_1M ) phys |=BT_PHY_LE_1M_RX ; -if (conn->le_tx_phy &HCI_LE_SET_PHY_2M ) +if (conn->le_tx_def_phys &HCI_LE_SET_PHY_2M ) phys |=BT_PHY_LE_2M_TX ; -if (conn->le_rx_phy &HCI_LE_SET_PHY_2M ) +if (conn->le_rx_def_phys &HCI_LE_SET_PHY_2M ) phys |=BT_PHY_LE_2M_RX ; -if (conn->le_tx_phy &HCI_LE_SET_PHY_CODED ) +if (conn->le_tx_def_phys &HCI_LE_SET_PHY_CODED ) phys |=BT_PHY_LE_CODED_TX ; -if (conn->le_rx_phy &HCI_LE_SET_PHY_CODED ) +if (conn->le_rx_def_phys &HCI_LE_SET_PHY_CODED ) phys |=BT_PHY_LE_CODED_RX ;break ;
net/bluetooth/hci_event.c +27 -3
@@ -6607,8 +6607,20 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data, conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (ev->handle));if (conn) { -if (!ev->status) -memcpy (conn->features[0 ], ev->features,8 ); +if (!ev->status) { +memcpy (conn->le_features, ev->features,8 ); + +/* Update supported PHYs */ +if (!(conn->le_features[1 ] &HCI_LE_PHY_2M )) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M ; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M ; + } + +if (!(conn->le_features[1 ] &HCI_LE_PHY_CODED )) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED ; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED ; + } + }if (conn->state ==BT_CONFIG ) {__u8 status;
@@ -7221,9 +7233,21 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,if (!conn) goto unlock; -if (!ev->status) +if (!ev->status) {memcpy (conn->le_features, ev->features,248 ); +/* Update supported PHYs */ +if (!(conn->le_features[1 ] &HCI_LE_PHY_2M )) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M ; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M ; + } + +if (!(conn->le_features[1 ] &HCI_LE_PHY_CODED )) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED ; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED ; + } + } +if (conn->state ==BT_CONFIG ) {__u8 status;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 373/781 PCI: Check parent for NULL in of_pci_bus_release_domain_nr()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Shtylyov <[email protected]>
[ Upstream commit f7245901de8978d829f80b3d8e36ed9a8fd18049 ]
of_pci_bus_find_domain_nr() allows its parent parameter to be NULL but of_pci_bus_release_domain_nr() (that undoes its effect) doesn't -- that means it's going to blow up while calling of_get_pci_domain_nr() if the parent parameter indeed happens to be NULL. Add the missing NULL check.
Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool.
--- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/pci.c +1 -1
@@ -6594,7 +6594,7 @@ static void of_pci_bus_release_domain_nr(struct device *parent, int domain_nr)return ;/* Release domain from IDA where it was allocated. */ -if (of_get_pci_domain_nr (parent->of_node) == domain_nr) +if (parent &&of_get_pci_domain_nr (parent->of_node) == domain_nr)ida_free (&pci_domain_nr_static_ida, domain_nr);else ida_free (&pci_domain_nr_dynamic_ida, domain_nr);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 374/781 wifi: ath10k: sdio: add missing lock protection in ath10k_sdio_fw_crashed_dump()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ziyi Guo <[email protected]>
[ Upstream commit e55ac348089e579fc224569c7bd90340bf2439f9 ]
ath10k_sdio_fw_crashed_dump() calls ath10k_coredump_new() which requires ar->dump_mutex to be held, as indicated by lockdep_assert_held() in that function. However, the SDIO implementation does not acquire this lock, unlike the PCI and SNOC implementations which properly hold the mutex.
Additionally, ar->stats.fw_crash_counter is documented as protected by ar->data_lock in core.h, but the SDIO implementation modifies it without holding this spinlock.
Add the missing mutex_lock()/mutex_unlock() around the coredump operations, and add spin_lock_bh()/spin_unlock_bh() around the fw_crash_counter increment, following the pattern used in ath10k_pci_fw_dump_work() and ath10k_snoc_fw_crashed_dump().
--- drivers/net/wireless/ath/ath10k/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/wireless/ath/ath10k/sdio.c +6 -0
@@ -2487,7 +2487,11 @@ void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)if (fast_dump)ath10k_bmi_start (ar); +mutex_lock (&ar->dump_mutex); + +spin_lock_bh (&ar->data_lock); ar->stats.fw_crash_counter++; +spin_unlock_bh (&ar->data_lock);ath10k_sdio_disable_intrs (ar);
@@ -2505,6 +2509,8 @@ void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)ath10k_sdio_enable_intrs (ar); +mutex_unlock (&ar->dump_mutex); +ath10k_core_start_recovery (ar); }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 375/781 wifi: ath11k: add usecase firmware handling based on device compatible
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miaoqing Pan <[email protected]>
[ Upstream commit c386a2b1068910538e87ef1cf2fc938ebf7e218f ]
For M.2 WLAN chips, there is no suitable DTS node to specify the firmware-name property. In addition, assigning firmware for the M.2 PCIe interface causes chips that do not use usecase specific firmware to fail. Therefore, abandoning the approach of specifying firmware in DTS. As an alternative, propose a static lookup table mapping device compatible to firmware names. Currently, only WCN6855 HW2.1 requires this.
However, support for the firmware-name property is retained to keep the ABI backwards compatible.
For details on usecase specific firmware, see:
https://lore.kernel.org/all/[email protected]/.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1
--- drivers/net/wireless/ath/ath11k/core.c | 27 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/core.h | 4 ++++ 2 files changed, 31 insertions(+)
drivers/net/wireless/ath/ath11k/core.c +27 -0
@@ -997,6 +997,33 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = { {} }; +static const struct __ath11k_core_usecase_firmware_table { +u32 hw_rev; +const char *compatible; +const char *firmware_name; +} ath11k_core_usecase_firmware_table[] = { + {ATH11K_HW_WCN6855_HW21 ,"qcom,lemans-evk" ,"nfa765" }, + {ATH11K_HW_WCN6855_HW21 ,"qcom,monaco-evk" ,"nfa765" }, + {ATH11K_HW_WCN6855_HW21 ,"qcom,hamoa-iot-evk" ,"nfa765" }, + {/* Sentinel */ } +}; + +const char *ath11k_core_get_usecase_firmware (struct ath11k_base *ab) +{ +const struct __ath11k_core_usecase_firmware_table *entry =NULL ; + + entry = ath11k_core_usecase_firmware_table; +while (entry->compatible) { +if (ab->hw_rev == entry->hw_rev && +of_machine_is_compatible (entry->compatible)) +return entry->firmware_name; + entry++; + } + +return NULL ; +} +EXPORT_SYMBOL (ath11k_core_get_usecase_firmware); +void ath11k_fw_stats_pdevs_free (struct list_head *head) {struct ath11k_fw_stats_pdev *i, *tmp;
drivers/net/wireless/ath/ath11k/core.h +4 -0
@@ -1275,6 +1275,7 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab);const struct firmware *ath11k_core_firmware_request (struct ath11k_base *ab,const char *filename); +const char *ath11k_core_get_usecase_firmware (struct ath11k_base *ab);static inline const char *ath11k_scan_state_str (enum ath11k_scan_state state) {
@@ -1329,6 +1330,9 @@ static inline void ath11k_core_create_firmware_path(struct ath11k_base *ab,of_property_read_string (ab->dev->of_node,"firmware-name" , &fw_name); +if (!fw_name) + fw_name =ath11k_core_get_usecase_firmware (ab); +if (fw_name &&strncmp (filename,"board" ,5 ))snprintf (buf, buf_len,"%s/%s/%s/%s" ,ATH11K_FW_DIR , ab->hw_params.fw.dir, fw_name, filename);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 376/781 wifi: ath12k: Fix index decrement when array_len is zero
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaradhana Sahu <[email protected]>
[ Upstream commit e4763898bb1325dbb3792961b6d607b5c6452d64 ]
Currently, print_array_to_buf_index() decrements index unconditionally. This may lead to invalid buffer access when array_len is zero.
Fix this by decrementing index only when array_len is non-zero.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
--- drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +5 -3
@@ -1,7 +1,7 @@// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. +* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/vmalloc.h>
@@ -29,8 +29,10 @@ print_array_to_buf_index(u8 *buf, u32 offset, const char *header, u32 stats_inde " %u:%u,", stats_index++, le32_to_cpu(array[i])); }/* To overwrite the last trailing comma */ - index--; - *(buf + offset + index) ='\0' ; +if (array_len >0 ) { + index--; + *(buf + offset + index) ='\0' ; + }if (footer) { index += scnprintf(buf + offset + index,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 377/781 wifi: ath12k: clear stale link mapping of ahvif->links_map
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaradhana Sahu <[email protected]>
[ Upstream commit 2c1ba9c2adf0fda96eaaebd8799268a7506a8fc9 ]
When an arvif is initialized in non-AP STA mode but MLO connection preparation fails before the arvif is created (arvif->is_created remains false), the error path attempts to delete all links. However, link deletion only executes when arvif->is_created is true. As a result, ahvif retains a stale entry of arvif that is initialized but not created.
When a new arvif is initialized with the same link id, this stale mapping triggers the following WARN_ON.
WARNING: drivers/net/wireless/ath/ath12k/mac.c:4271 at ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k], CPU#3: wpa_supplicant/275 Call trace: ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k] (P) drv_change_vif_links+0xbc/0x1a4 [mac80211] ieee80211_vif_update_links+0x54c/0x6a0 [mac80211] ieee80211_vif_set_links+0x40/0x70 [mac80211] ieee80211_prep_connection+0x84/0x450 [mac80211] ieee80211_mgd_auth+0x200/0x480 [mac80211] ieee80211_auth+0x14/0x20 [mac80211] cfg80211_mlme_auth+0x90/0xf0 [cfg80211] nl80211_authenticate+0x32c/0x380 [cfg80211] genl_family_rcv_msg_doit+0xc8/0x134
Fix this issue by unassigning the link vif and clearing ahvif->links_map if arvif is only initialized but not created.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
--- drivers/net/wireless/ath/ath12k/mac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/ath/ath12k/mac.c +3 -1
@@ -4175,8 +4175,10 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw,if (WARN_ON (!arvif))return -EINVAL ; -if (!arvif->is_created) +if (!arvif->is_created) { +ath12k_mac_unassign_link_vif (arvif);continue ; + }if (WARN_ON (!arvif->ar))return -EINVAL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 378/781 PCI: Initialize RCB from pci_configure_device()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Håkon Bugge <[email protected]>
[ Upstream commit 1a6845aaa6de81f95959b380b45de8f10d6a8502 ]
Commit e42010d8207f ("PCI: Set Read Completion Boundary to 128 iff Root Port supports it (_HPX)") worked around a bogus _HPX type 2 record, which caused program_hpx_type2() to set the RCB in an endpoint even though the Root Port did not have the RCB bit set.
e42010d8207f fixed that by setting the RCB in the endpoint only when it was set in the Root Port.
In retrospect, program_hpx_type2() is intended for AER-related settings, and the RCB should be configured elsewhere so it doesn't depend on the presence or contents of an _HPX record.
Explicitly program the RCB from pci_configure_device() so it matches the Root Port's RCB. The Root Port may not be visible to virtualized guests; in that case, leave RCB alone.
--- drivers/pci/probe.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
drivers/pci/probe.c +32 -0
@@ -2411,6 +2411,37 @@ static void pci_configure_serr(struct pci_dev *dev) } } +static void pci_configure_rcb (struct pci_dev *dev) +{ +struct pci_dev *rp; +u16 rp_lnkctl; + +/* +* Per PCIe r7.0, sec 7.5.3.7, RCB is only meaningful in Root Ports +* (where it is read-only), Endpoints, and Bridges. It may only be +* set for Endpoints and Bridges if it is set in the Root Port. For +* Endpoints, it is 'RsvdP' for Virtual Functions. +*/ +if (!pci_is_pcie (dev) || +pci_pcie_type (dev) ==PCI_EXP_TYPE_ROOT_PORT || +pci_pcie_type (dev) ==PCI_EXP_TYPE_UPSTREAM || +pci_pcie_type (dev) ==PCI_EXP_TYPE_DOWNSTREAM || +pci_pcie_type (dev) ==PCI_EXP_TYPE_RC_EC || + dev->is_virtfn) +return ; + +/* Root Port often not visible to virtualized guests */ + rp =pcie_find_root_port (dev); +if (!rp) +return ; + +pcie_capability_read_word (rp,PCI_EXP_LNKCTL , &rp_lnkctl); +pcie_capability_clear_and_set_word (dev,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_RCB , + (rp_lnkctl &PCI_EXP_LNKCTL_RCB ) ? +PCI_EXP_LNKCTL_RCB :0 ); +} +static void pci_configure_device (struct pci_dev *dev) {pci_configure_mps (dev);
@@ -2420,6 +2451,7 @@ static void pci_configure_device(struct pci_dev *dev)pci_configure_aspm_l1ss (dev);pci_configure_eetlp_prefix (dev);pci_configure_serr (dev); +pci_configure_rcb (dev);pci_acpi_program_hp_params (dev); }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 379/781 PCI/ACPI: Restrict program_hpx_type2() to AER bits
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Håkon Bugge <[email protected]>
[ Upstream commit 9abf79c8d7b40db0e5a34aa8c744ea60ff9a3fcf ]
Previously program_hpx_type2() applied PCIe settings unconditionally, which could incorrectly change bits like Extended Tag Field Enable and Enable Relaxed Ordering.
When _HPX was added to ACPI r3.0, the intent of the PCIe Setting Record (Type 2) in sec 6.2.7.3 was to configure AER registers when the OS does not own the AER Capability:
The PCI Express setting record contains ... [the AER] Uncorrectable Error Mask, Uncorrectable Error Severity, Correctable Error Mask ... to be used when configuring registers in the Advanced Error Reporting Extended Capability Structure ...
OSPM [1] will only evaluate _HPX with Setting Record – Type 2 if OSPM is not controlling the PCI Express Advanced Error Reporting capability.
ACPI r3.0b, sec 6.2.7.3, added more AER registers, including registers in the PCIe Capability with AER-related bits, and the restriction that the OS use this only when it owns PCIe native hotplug:
... when configuring PCI Express registers in the Advanced Error Reporting Extended Capability Structure *or PCI Express Capability Structure* ...
An OS that has assumed ownership of native hot plug but does not ... have ownership of the AER register set must use ... the Type 2 record to program the AER registers ...
However, since the Type 2 record also includes register bits that have functions other than AER, the OS must ignore values ... that are not applicable.
Restrict program_hpx_type2() to only the intended purpose:
- Apply settings only when OS owns PCIe native hotplug but not AER,
- Only touch the AER-related bits (Error Reporting Enables) in Device Control
- Don't touch Link Control at all, since nothing there seems AER-related, but log _HPX settings for debugging purposes
Note that Read Completion Boundary is now configured elsewhere, since it is unrelated to _HPX.
[1] Operating System-directed configuration and Power Management
--- drivers/pci/pci-acpi.c | 59 +++++++++++++++++------------------------- drivers/pci/pci.h | 3 +++ drivers/pci/pcie/aer.c | 3 --- 3 files changed, 27 insertions(+), 38 deletions(-)
drivers/pci/pci-acpi.c +24 -35
@@ -271,21 +271,6 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,return AE_OK ; } -static bool pcie_root_rcb_set (struct pci_dev *dev) -{ -struct pci_dev *rp =pcie_find_root_port (dev); -u16 lnkctl; - -if (!rp) -return false; - -pcie_capability_read_word (rp,PCI_EXP_LNKCTL , &lnkctl); -if (lnkctl &PCI_EXP_LNKCTL_RCB ) -return true; - -return false; -} -/* _HPX PCI Express Setting Record (Type 2) */ struct hpx_type2 {u32 revision;
@@ -311,6 +296,7 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) {int pos;u32 reg32; +const struct pci_host_bridge *host;if (!hpx)return ;
@@ -318,6 +304,15 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)if (!pci_is_pcie (dev))return ; + host =pci_find_host_bridge (dev->bus); + +/* +* Only do the _HPX Type 2 programming if OS owns PCIe native +* hotplug but not AER. +*/ +if (!host->native_pcie_hotplug || host->native_aer) +return ; +if (hpx->revision >1 ) {pci_warn (dev,"PCIe settings rev %d not supported\n" , hpx->revision);
@@ -325,33 +320,27 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) }/* -* Don't allow _HPX to change MPS or MRRS settings. We manage -* those to make sure they're consistent with the rest of the -* platform. +* We only allow _HPX to program DEVCTL bits related to AER, namely +* PCI_EXP_DEVCTL_CERE, PCI_EXP_DEVCTL_NFERE, PCI_EXP_DEVCTL_FERE, +* and PCI_EXP_DEVCTL_URRE. +* +* The rest of DEVCTL is managed by the OS to make sure it's +* consistent with the rest of the platform. */ - hpx->pci_exp_devctl_and |=PCI_EXP_DEVCTL_PAYLOAD | -PCI_EXP_DEVCTL_READRQ ; - hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD | -PCI_EXP_DEVCTL_READRQ ); + hpx->pci_exp_devctl_and |= ~PCI_EXP_AER_FLAGS ; + hpx->pci_exp_devctl_or &=PCI_EXP_AER_FLAGS ;/* Initialize Device Control Register */ pcie_capability_clear_and_set_word (dev,PCI_EXP_DEVCTL , ~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or); -/* Initialize Link Control Register */ +/* Log if _HPX attempts to modify Link Control Register */ if (pcie_cap_has_lnkctl (dev)) { - -/* -* If the Root Port supports Read Completion Boundary of -* 128, set RCB to 128. Otherwise, clear it. -*/ - hpx->pci_exp_lnkctl_and |=PCI_EXP_LNKCTL_RCB ; - hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB ; -if (pcie_root_rcb_set (dev)) - hpx->pci_exp_lnkctl_or |=PCI_EXP_LNKCTL_RCB ; - -pcie_capability_clear_and_set_word (dev,PCI_EXP_LNKCTL , - ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or); +if (hpx->pci_exp_lnkctl_and !=0xffff || + hpx->pci_exp_lnkctl_or !=0 ) +pci_info (dev,"_HPX attempts Link Control setting (AND %#06x OR %#06x)\n" , + hpx->pci_exp_lnkctl_and, + hpx->pci_exp_lnkctl_or); }/* Find Advanced Error Reporting Enhanced Capability */
drivers/pci/pci.h +3 -0
@@ -88,6 +88,9 @@ struct pcie_tlp_log;#define PCI_BUS_BRIDGE_MEM_WINDOW 1#define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2 +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) +extern const unsigned char pcie_link_speed[];extern bool pci_early_dump;
drivers/pci/pcie/aer.c +0 -3
@@ -239,9 +239,6 @@ void pcie_ecrc_get_policy(char *str) }#endif /* CONFIG_PCIE_ECRC */ -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) -int pcie_aer_is_native (struct pci_dev *dev) {struct pci_host_bridge *host =pci_find_host_bridge (dev->bus);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 380/781 Revert "net/smc: Introduce TCP ULP support"
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: D. Wythe <[email protected]>
[ Upstream commit df31a6b0a3057e66994ad6ccf5d95b9b9514f033 ]
This reverts commit d7cd421da9da2cc7b4d25b8537f66db5c8331c40.
As reported by Al Viro, the TCP ULP support for SMC is fundamentally broken. The implementation attempts to convert an active TCP socket into an SMC socket by modifying the underlying struct file, dentry, and inode in-place, which violates core VFS invariants that assume these structures are immutable for an open file, creating a risk of use after free errors and general system instability.
Given the severity of this design flaw and the fact that cleaner alternatives (e.g., LD_PRELOAD, BPF) exist for legacy application transparency, the correct course of action is to remove this feature entirely.
--- net/smc/af_smc.c | 91 +++--------------------------------------------- 1 file changed, 4 insertions(+), 87 deletions(-)
net/smc/af_smc.c +4 -87
@@ -3357,11 +3357,10 @@ int smc_create_clcsk(struct net *net, struct sock *sk, int family)return 0 ; } -static int __smc_create (struct net *net,struct socket *sock,int protocol, -int kern,struct socket *clcsock) +static int smc_create (struct net *net,struct socket *sock,int protocol, +int kern) {int family = (protocol ==SMCPROTO_SMC6 ) ?PF_INET6 :PF_INET ; -struct smc_sock *smc;struct sock *sk;int rc;
@@ -3380,15 +3379,7 @@ static int __smc_create(struct net *net, struct socket *sock, int protocol,if (!sk) goto out; -/* create internal TCP socket for CLC handshake and fallback */ - smc =smc_sk (sk); - - rc =0 ; -if (clcsock) - smc->clcsock = clcsock; -else - rc =smc_create_clcsk (net, sk, family); - + rc =smc_create_clcsk (net, sk, family);if (rc) {sk_common_release (sk); sock->sk =NULL ;
@@ -3397,76 +3388,12 @@ static int __smc_create(struct net *net, struct socket *sock, int protocol,return rc; } -static int smc_create (struct net *net,struct socket *sock,int protocol, -int kern) -{ -return __smc_create (net, sock, protocol, kern,NULL ); -} -static const struct net_proto_family smc_sock_family_ops = { .family =PF_SMC , .owner =THIS_MODULE , .create = smc_create, }; -static int smc_ulp_init (struct sock *sk) -{ -struct socket *tcp = sk->sk_socket; -struct net *net =sock_net (sk); -struct socket *smcsock; -int protocol, ret; - -/* only TCP can be replaced */ -if (tcp->type !=SOCK_STREAM || sk->sk_protocol !=IPPROTO_TCP || - (sk->sk_family !=AF_INET && sk->sk_family !=AF_INET6 )) -return -ESOCKTNOSUPPORT ; -/* don't handle wq now */ -if (tcp->state !=SS_UNCONNECTED || !tcp->file || tcp->wq.fasync_list) -return -ENOTCONN ; - -if (sk->sk_family ==AF_INET ) - protocol =SMCPROTO_SMC ; -else - protocol =SMCPROTO_SMC6 ; - - smcsock =sock_alloc (); -if (!smcsock) -return -ENFILE ; - - smcsock->type =SOCK_STREAM ; -__module_get (THIS_MODULE );/* tried in __tcp_ulp_find_autoload */ - ret =__smc_create (net, smcsock, protocol,1 , tcp); -if (ret) { -sock_release (smcsock);/* module_put() which ops won't be NULL */ -return ret; - } - -/* replace tcp socket to smc */ - smcsock->file = tcp->file; - smcsock->file->private_data = smcsock; - smcsock->file->f_inode =SOCK_INODE (smcsock);/* replace inode when sock_close */ - smcsock->file->f_path.dentry->d_inode =SOCK_INODE (smcsock);/* dput() in __fput */ - tcp->file =NULL ; - -return ret; -} - -static void smc_ulp_clone (const struct request_sock *req,struct sock *newsk, -const gfp_t priority) -{ -struct inet_connection_sock *icsk =inet_csk (newsk); - -/* don't inherit ulp ops to child when listen */ - icsk->icsk_ulp_ops =NULL ; -} - -static struct tcp_ulp_ops smc_ulp_ops__read_mostly = { - .name ="smc" , - .owner =THIS_MODULE , - .init = smc_ulp_init, - .clone = smc_ulp_clone, -}; -unsigned int smc_net_id;static __net_init int smc_net_init (struct net *net)
@@ -3589,16 +3516,10 @@ static int __init smc_init(void)pr_err ("%s: ib_register fails with %d\n" , __func__, rc); goto out_sock; } - - rc =tcp_register_ulp (&smc_ulp_ops); -if (rc) { -pr_err ("%s: tcp_ulp_register fails with %d\n" , __func__, rc); - goto out_ib; - } rc =smc_inet_init ();if (rc) {pr_err ("%s: smc_inet_init fails with %d\n" , __func__, rc); - goto out_ulp; + goto out_ib; } rc =bpf_smc_hs_ctrl_init ();if (rc) {
@@ -3610,8 +3531,6 @@ static int __init smc_init(void)return 0 ; out_inet:smc_inet_exit (); -out_ulp: -tcp_unregister_ulp (&smc_ulp_ops); out_ib:smc_ib_unregister_client (); out_sock:
@@ -3647,7 +3566,6 @@ static void __exit smc_exit(void) {static_branch_disable (&tcp_have_smc);smc_inet_exit (); -tcp_unregister_ulp (&smc_ulp_ops);sock_unregister (PF_SMC );smc_core_exit ();smc_ib_unregister_client ();
@@ -3672,7 +3590,6 @@ MODULE_AUTHOR("Ursula Braun <[email protected]>");MODULE_DESCRIPTION ("smc socket address family" );MODULE_LICENSE ("GPL" );MODULE_ALIAS_NETPROTO (PF_SMC ); -MODULE_ALIAS_TCP_ULP ("smc" );/* 256 for IPPROTO_SMC and 1 for SOCK_STREAM */ MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_INET ,256 ,1 );#if IS_ENABLED (CONFIG_IPV6 )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 381/781 selftests/mm: fix usage of FORCE_READ() in cow tests
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Brodsky <[email protected]>
[ Upstream commit bce1dabd310e87fefe0645fec9ba98b84d37e418 ]
Commit 5bbc2b785e63 ("selftests/mm: fix FORCE_READ to read input value correctly") modified FORCE_READ() to take a value instead of a pointer. It also changed most of the call sites accordingly, but missed many of them in cow.c. In those cases, we ended up with the pointer itself being read, not the memory it points to.
No failure occurred as a result, so it looks like the tests work just fine without faulting in. However, the huge_zeropage tests explicitly check that pages are populated, so those became skipped.
Convert all the remaining FORCE_READ() to fault in the mapped page, as was originally intended. This allows the huge_zeropage tests to run again (3 tests in total).
--- tools/testing/selftests/mm/cow.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
tools/testing/selftests/mm/cow.c +8 -8
@@ -1612,8 +1612,8 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc)* the first sub-page and test if we get another sub-page populated * automatically. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);if (!pagemap_is_populated (pagemap_fd, mem + pagesize) || !pagemap_is_populated (pagemap_fd, smem + pagesize)) {ksft_test_result_skip ("Did not get THPs populated\n" );
@@ -1663,8 +1663,8 @@ static void run_with_memfd(non_anon_test_fn fn, const char *desc) }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, pagesize); munmap:
@@ -1719,8 +1719,8 @@ static void run_with_tmpfile(non_anon_test_fn fn, const char *desc) }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, pagesize); munmap:
@@ -1773,8 +1773,8 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc, }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, hugetlbsize); munmap:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 382/781 selftests/mm: fix faulting-in code in pagemap_ioctl test
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Brodsky <[email protected]>
[ Upstream commit 7e938f00b00319510ae097e20b7487dfa578d53f ]
One of the pagemap_ioctl tests attempts to fault in pages by memcpy()'ing them to an unused buffer. This probably worked originally, but since commit 46036188ea1f ("selftests/mm: build with -O2") the compiler is free to optimise away that unused buffer and the memcpy() with it. As a result there might not be any resident page in the mapping and the test may fail.
We don't need to copy all that memory anyway. Just fault in every page.
While at it also make sure to compute the number of pages once using simple integer arithmetic instead of ceilf() and implicit conversions.
--- tools/testing/selftests/mm/pagemap_ioctl.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
tools/testing/selftests/mm/pagemap_ioctl.c +4 -5
@@ -1052,11 +1052,10 @@ static void test_simple(void)int sanity_tests (void ) {unsigned long long mem_size, vec_size; -long ret, fd, i, buf_size; +long ret, fd, i, buf_size, nr_pages;struct page_region *vec;char *mem, *fmem;struct stat sbuf; -char *tmp_buf;/* 1. wrong operation */ mem_size =10 * page_size;
@@ -1167,14 +1166,14 @@ int sanity_tests(void)if (fmem ==MAP_FAILED )ksft_exit_fail_msg ("error nomem %d %s\n" , errno,strerror (errno)); - tmp_buf =malloc (sbuf.st_size); -memcpy (tmp_buf, fmem, sbuf.st_size); + nr_pages = (sbuf.st_size + page_size -1 ) / page_size; +force_read_pages (fmem, nr_pages, page_size); ret =pagemap_ioctl (fmem, sbuf.st_size, vec, vec_size,0 ,0 ,0 ,PAGEMAP_NON_WRITTEN_BITS ,0 ,PAGEMAP_NON_WRITTEN_BITS );ksft_test_result (ret >=0 && vec[0 ].start == (uintptr_t )fmem && -LEN (vec[0 ]) ==ceilf ((float )sbuf.st_size/page_size) && +LEN (vec[0 ]) == nr_pages && (vec[0 ].categories &PAGE_IS_FILE ),"%s Memory mapped file\n" , __func__);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 383/781 ipc: dont audit capability check in ipc_permissions()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ondrej Mosnacek <[email protected]>
[ Upstream commit 8924336531e21b187d724b5fdf5277269c9ec22c ]
The IPC sysctls implement the ctl_table_root::permissions hook and they override the file access mode based on the CAP_CHECKPOINT_RESTORE capability, which is being checked regardless of whether any access is actually denied or not, so if an LSM denies the capability, an audit record may be logged even when access is in fact granted.
It wouldn't be viable to restructure the sysctl permission logic to only check the capability when the access would be actually denied if it's not granted. Thus, do the same as in net_ctl_permissions() (net/sysctl_net.c)
- switch from ns_capable() to ns_capable_noaudit(), so that the check never emits an audit record.
--- include/linux/capability.h | 6 ++++++ ipc/ipc_sysctl.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-)
include/linux/capability.h +6 -0
@@ -203,6 +203,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)ns_capable (ns,CAP_SYS_ADMIN ); } +static inline bool checkpoint_restore_ns_capable_noaudit (struct user_namespace *ns) +{ +return ns_capable_noaudit (ns,CAP_CHECKPOINT_RESTORE ) || +ns_capable_noaudit (ns,CAP_SYS_ADMIN ); +} +/* audit system wants to get cap info from files as well */ int get_vfs_caps_from_disk(struct mnt_idmap *idmap, const struct dentry *dentry,
ipc/ipc_sysctl.c +1 -1
@@ -214,7 +214,7 @@ static int ipc_permissions(struct ctl_table_header *head, const struct ctl_tableif (((table->data == &ns->ids[IPC_SEM_IDS ].next_id) || (table->data == &ns->ids[IPC_MSG_IDS ].next_id) || (table->data == &ns->ids[IPC_SHM_IDS ].next_id)) && -checkpoint_restore_ns_capable (ns->user_ns)) +checkpoint_restore_ns_capable_noaudit (ns->user_ns)) mode =0666 ; else#endif
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 384/781 ucount: check for CAP_SYS_RESOURCE using ns_capable_noaudit()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ondrej Mosnacek <[email protected]>
[ Upstream commit 0895a000e4fff9e950a7894210db45973e485c35 ]
The user.* sysctls implement the ctl_table_root::permissions hook and they override the file access mode based on the CAP_SYS_RESOURCE capability (at most rwx if capable, at most r-- if not). The capability is being checked unconditionally, so if an LSM denies the capability, an audit record may be logged even when access is in fact granted.
Given the logic in the set_permissions() function in kernel/ucount.c and the unfortunate way the permission checking is implemented, it doesn't seem viable to avoid false positive denials by deferring the capability check. Thus, do the same as in net_ctl_permissions() (net/sysctl_net.c) - switch from ns_capable() to ns_capable_noaudit(), so that the check never logs an audit record.
--- kernel/ucount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/ucount.c +1 -1
@@ -47,7 +47,7 @@ static int set_permissions(struct ctl_table_header *head,int mode;/* Allow users with CAP_SYS_RESOURCE unrestrained access */ -if (ns_capable (user_ns,CAP_SYS_RESOURCE )) +if (ns_capable_noaudit (user_ns,CAP_SYS_RESOURCE )) mode = (table->mode &S_IRWXU ) >>6 ;else /* Allow all others at most read-only access */
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 385/781 jfs: avoid -Wtautological-constant-out-of-range-compare warning
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnd Bergmann <[email protected]>
[ Upstream commit 7833570dae833028337bb53b7f389825b910c100 ]
A recent change for the range check started triggering a clang warning:
fs/jfs/jfs_dtree.c:2906:31: error: result of comparison of constant 128 with expression of type 's8' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
2906 | if (stbl[i] < 0 || stbl[i] >= DTPAGEMAXSLOT) {
| ~~~~~~~ ^ ~~~~~~~~~~~~~
fs/jfs/jfs_dtree.c:3111:30: error: result of comparison of constant 128 with expression of type 's8' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
3111 | if (stbl[0] < 0 || stbl[0] >= DTPAGEMAXSLOT) {
| ~~~~~~~ ^ ~~~~~~~~~~~~~Both the old and the new check were useless, but the previous version apparently did not lead to the warning.
Remove the extraneous range check for simplicity.
--- fs/jfs/jfs_dtree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/jfs/jfs_dtree.c +2 -2
@@ -2903,7 +2903,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx) stbl =DT_GETSTBL (p);for (i = index; i < p->header.nextindex; i++) { -if (stbl[i] <0 || stbl[i] >=DTPAGEMAXSLOT ) { +if (stbl[i] <0 ) {jfs_err ("JFS: Invalid stbl[%d] = %d for inode %ld, block = %lld" , i, stbl[i], (long )ip->i_ino, (long long )bn);free_page (dirent_buf);
@@ -3108,7 +3108,7 @@ static int dtReadFirst(struct inode *ip, struct btstack * btstack)/* get the leftmost entry */ stbl =DT_GETSTBL (p); -if (stbl[0 ] <0 || stbl[0 ] >=DTPAGEMAXSLOT ) { +if (stbl[0 ] <0 ) {DT_PUTPAGE (mp);jfs_error (ip->i_sb,"stbl[0] out of bound\n" );return -EIO ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 386/781 PCI: s32g: Skip Root Port removal during success
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Guittot <[email protected]>
[ Upstream commit b79e0875fe8144fcb09e4fc1cf386cb3b2262480 ]
Currently, s32g_pcie_parse_ports() exercises the 'err_port' path even during the success case. This results in ports getting deleted after successful parsing of Root Ports.
Hence, skip the removal of Root Ports during success.
--- drivers/pci/controller/dwc/pcie-nxp-s32g.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/pci/controller/dwc/pcie-nxp-s32g.c +4 -4
@@ -282,12 +282,12 @@ static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) ret =s32g_pcie_parse_port (s32g_pp, of_port);if (ret) - goto err_port; +break ; } -err_port: -list_for_each_entry_safe (port, tmp, &s32g_pp->ports, list) -list_del (&port->list); +if (ret) +list_for_each_entry_safe (port, tmp, &s32g_pp->ports, list) +list_del (&port->list);return ret; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 387/781 tcp: ECT_1_NEGOTIATION and NEEDS_ACCECN identifiers
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit 100f946b8d44b64bc0b8a8c30d283105031c0a77 ]
Two flags for congestion control (CC) module are added in this patch related to AccECN negotiation. First, a new flag (TCP_CONG_NEEDS_ACCECN) defines that the CC expects to negotiate AccECN functionality using the ECE, CWR and AE flags in the TCP header.
Second, during ECN negotiation, ECT(0) in the IP header is used. This patch enables CC to control whether ECT(0) or ECT(1) should be used on a per-segment basis. A new flag (TCP_CONG_ECT_1_NEGOTIATION) defines the expected ECT value in the IP header by the CA when not-yet initialized for the connection.
The detailed AccECN negotiaotn can be found in IETF RFC9768.
Stable-dep-of: c5ff6b837159 ("tcp: accecn: handle unexpected AccECN negotiation feedback")
--- include/net/inet_ecn.h | 20 +++++++++++++++++--- include/net/tcp.h | 21 ++++++++++++++++++++- include/net/tcp_ecn.h | 13 ++++++++++--- net/ipv4/tcp_cong.c | 5 +++-- net/ipv4/tcp_input.c | 3 ++- 5 files changed, 52 insertions(+), 10 deletions(-)
include/net/inet_ecn.h +17 -3
@@ -51,11 +51,25 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)return outer; } +/* Apply either ECT(0) or ECT(1) */ +static inline void __INET_ECN_xmit (struct sock *sk,bool use_ect_1) +{ +__u8 ect = use_ect_1 ?INET_ECN_ECT_1 :INET_ECN_ECT_0 ; + +/* Mask the complete byte in case the connection alternates between +* ECT(0) and ECT(1). +*/ +inet_sk (sk)->tos &= ~INET_ECN_MASK ; +inet_sk (sk)->tos |= ect; +if (inet6_sk (sk)) { +inet6_sk (sk)->tclass &= ~INET_ECN_MASK ; +inet6_sk (sk)->tclass |= ect; + } +} +static inline void INET_ECN_xmit (struct sock *sk) { -inet_sk (sk)->tos |=INET_ECN_ECT_0 ; -if (inet6_sk (sk) !=NULL ) -inet6_sk (sk)->tclass |=INET_ECN_ECT_0 ; +__INET_ECN_xmit (sk, false); }static inline void INET_ECN_dontxmit (struct sock *sk)
include/net/tcp.h +20 -1
@@ -1203,7 +1203,12 @@ enum tcp_ca_ack_event_flags {#define TCP_CONG_NON_RESTRICTED BIT(0)/* Requires ECN/ECT set on all packets */ #define TCP_CONG_NEEDS_ECN BIT(1) -#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN) +/* Require successfully negotiated AccECN capability */ +#define TCP_CONG_NEEDS_ACCECN BIT(2) +/* Use ECT(1) instead of ECT(0) while the CA is uninitialized */ +#define TCP_CONG_ECT_1_NEGOTIATION BIT(3) +#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \ + TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION)union tcp_cc_info ;
@@ -1335,6 +1340,20 @@ static inline bool tcp_ca_needs_ecn(const struct sock *sk)return icsk->icsk_ca_ops->flags &TCP_CONG_NEEDS_ECN ; } +static inline bool tcp_ca_needs_accecn (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_NEEDS_ACCECN ; +} + +static inline bool tcp_ca_ect_1_negotiation (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_ECT_1_NEGOTIATION ; +} +static inline void tcp_ca_event (struct sock *sk,const enum tcp_ca_event event) {const struct inet_connection_sock *icsk =inet_csk (sk);
include/net/tcp_ecn.h +10 -3
@@ -31,6 +31,12 @@ enum tcp_accecn_option { TCP_ACCECN_OPTION_FULL = 2, }; +/* Apply either ECT(0) or ECT(1) based on TCP_CONG_ECT_1_NEGOTIATION flag */ +static inline void INET_ECN_xmit_ect_1_negotiation (struct sock *sk) +{ +__INET_ECN_xmit (sk,tcp_ca_ect_1_negotiation (sk)); +} +static inline void tcp_ecn_queue_cwr (struct tcp_sock *tp) {/* Do not set CWR if in AccECN mode! */
@@ -561,7 +567,7 @@ static inline void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)TCP_SKB_CB (skb)->tcp_flags &= ~TCPHDR_ECE ; else if (tcp_ca_needs_ecn(sk) || tcp_bpf_ca_needs_ecn(sk)) - INET_ECN_xmit(sk); + INET_ECN_xmit_ect_1_negotiation(sk);if (tp->ecn_flags &TCP_ECN_MODE_ACCECN ) {TCP_SKB_CB (skb)->tcp_flags &= ~TCPHDR_ACE ;
@@ -579,7 +585,8 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)bool use_ecn, use_accecn;u8 tcp_ecn =READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_ecn); - use_accecn = tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ACCECN ; + use_accecn = tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ACCECN || +tcp_ca_needs_accecn (sk); use_ecn = tcp_ecn ==TCP_ECN_IN_ECN_OUT_ECN || tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ECN ||tcp_ca_needs_ecn (sk) || bpf_needs_ecn || use_accecn;
@@ -595,7 +602,7 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)if (use_ecn) {if (tcp_ca_needs_ecn (sk) || bpf_needs_ecn) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);TCP_SKB_CB (skb)->tcp_flags |=TCPHDR_ECE |TCPHDR_CWR ;if (use_accecn) {
net/ipv4/tcp_cong.c +3 -2
@@ -16,6 +16,7 @@#include <linux/gfp.h> #include <linux/jhash.h> #include <net/tcp.h> +#include <net/tcp_ecn.h> #include <trace/events/tcp.h> static DEFINE_SPINLOCK (tcp_cong_list_lock);
@@ -227,7 +228,7 @@ void tcp_assign_congestion_control(struct sock *sk)memset (icsk->icsk_ca_priv,0 ,sizeof (icsk->icsk_ca_priv));if (ca->flags &TCP_CONG_NEEDS_ECN ) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);else INET_ECN_dontxmit (sk); }
@@ -257,7 +258,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,memset (icsk->icsk_ca_priv,0 ,sizeof (icsk->icsk_ca_priv));if (ca->flags &TCP_CONG_NEEDS_ECN ) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);else INET_ECN_dontxmit (sk);
net/ipv4/tcp_input.c +2 -1
@@ -7248,7 +7248,8 @@ static void tcp_ecn_create_request(struct request_sock *req,u32 ecn_ok_dst;if (tcp_accecn_syn_requested (th) && -READ_ONCE (net->ipv4.sysctl_tcp_ecn) >=3 ) { + (READ_ONCE (net->ipv4.sysctl_tcp_ecn) >=3 || +tcp_ca_needs_accecn (listen_sk))) {inet_rsk (req)->ecn_ok =1 ;tcp_rsk (req)->accecn_ok =1 ; tcp_rsk(req)->syn_ect_rcv = TCP_SKB_CB(skb)->ip_dsfield &
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 388/781 tcp: disable RFC3168 fallback identifier for CC modules
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit e68c28f22f46ecfdec3656ae785dd8ccbb4d557d ]
When AccECN is not successfully negociated for a TCP flow, it defaults fallback to classic ECN (RFC3168). However, L4S service will fallback to non-ECN.
This patch enables congestion control module to control whether it should not fallback to classic ECN after unsuccessful AccECN negotiation. A new CA module flag (TCP_CONG_NO_FALLBACK_RFC3168) identifies this behavior expected by the CA.
Stable-dep-of: c5ff6b837159 ("tcp: accecn: handle unexpected AccECN negotiation feedback")
--- include/net/tcp.h | 12 +++++++++++- include/net/tcp_ecn.h | 11 ++++++++--- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_minisocks.c | 7 ++++--- 4 files changed, 24 insertions(+), 8 deletions(-)
include/net/tcp.h +11 -1
@@ -1207,8 +1207,11 @@ enum tcp_ca_ack_event_flags {#define TCP_CONG_NEEDS_ACCECN BIT(2)/* Use ECT(1) instead of ECT(0) while the CA is uninitialized */ #define TCP_CONG_ECT_1_NEGOTIATION BIT(3) +/* Cannot fallback to RFC3168 during AccECN negotiation */ +#define TCP_CONG_NO_FALLBACK_RFC3168 BIT(4)#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \ - TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION) + TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION | \ + TCP_CONG_NO_FALLBACK_RFC3168)union tcp_cc_info ;
@@ -1354,6 +1357,13 @@ static inline bool tcp_ca_ect_1_negotiation(const struct sock *sk)return icsk->icsk_ca_ops->flags &TCP_CONG_ECT_1_NEGOTIATION ; } +static inline bool tcp_ca_no_fallback_rfc3168 (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_NO_FALLBACK_RFC3168 ; +} +static inline void tcp_ca_event (struct sock *sk,const enum tcp_ca_event event) {const struct inet_connection_sock *icsk =inet_csk (sk);
include/net/tcp_ecn.h +8 -3
@@ -507,7 +507,9 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb* | ECN | AccECN | 0 0 1 | Classic ECN | * +========+========+============+=============+ */ -if (tcp_ecn_mode_pending (tp)) +if (tcp_ca_no_fallback_rfc3168 (sk)) +tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); +else if (tcp_ecn_mode_pending (tp))/* Downgrade from AccECN, or requested initially */ tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 );break ;
@@ -531,9 +533,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb } } -static inline void tcp_ecn_rcv_syn (struct tcp_sock *tp,const struct tcphdr *th, +static inline void tcp_ecn_rcv_syn (struct sock *sk,const struct tcphdr *th,const struct sk_buff *skb) { +struct tcp_sock *tp =tcp_sk (sk); +if (tcp_ecn_mode_pending (tp)) {if (!tcp_accecn_syn_requested (th)) {/* Downgrade to classic ECN feedback */
@@ -545,7 +549,8 @@ static inline void tcp_ecn_rcv_syn(struct tcp_sock *tp, const struct tcphdr *th,tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); } } -if (tcp_ecn_mode_rfc3168 (tp) && (!th->ece || !th->cwr)) +if (tcp_ecn_mode_rfc3168 (tp) && + (!th->ece || !th->cwr ||tcp_ca_no_fallback_rfc3168 (sk)))tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); }
net/ipv4/tcp_input.c +1 -1
@@ -6843,7 +6843,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_wl1 =TCP_SKB_CB (skb)->seq; tp->max_window = tp->snd_wnd; -tcp_ecn_rcv_syn (tp, th, skb); +tcp_ecn_rcv_syn (sk, th, skb);tcp_mtup_init (sk);tcp_sync_mss (sk, icsk->icsk_pmtu_cookie);
net/ipv4/tcp_minisocks.c +4 -3
@@ -485,9 +485,10 @@ static void tcp_ecn_openreq_child(struct sock *sk, tp->accecn_opt_demand =1 ;tcp_ecn_received_counters_payload (sk, skb); }else { -tcp_ecn_mode_set (tp,inet_rsk (req)->ecn_ok ? -TCP_ECN_MODE_RFC3168 : -TCP_ECN_DISABLED ); +if (inet_rsk (req)->ecn_ok && !tcp_ca_no_fallback_rfc3168 (sk)) +tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 ); +else +tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 389/781 tcp: accecn: handle unexpected AccECN negotiation feedback
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit c5ff6b83715919767f181f13e992b5055812a194 ]
According to Sections 3.1.2 and 3.1.3 of AccECN spec (RFC9768).
In Section 3.1.2, it says an AccECN implementation has no need to recognize or support the Server response labelled 'Nonce' or ECN-nonce feedback more generally, as RFC 3540 has been reclassified as Historic. AccECN is compatible with alternative ECN feedback integrity approaches to the nonce. The SYN/ACK labelled 'Nonce' with (AE,CWR,ECE) = (1,0,1) is reserved for future use. A TCP Client (A) that receives such a SYN/ACK follows the procedure for forward compatibility given in Section 3.1.3.
Then in Section 3.1.3, it says if a TCP Client has sent a SYN requesting AccECN feedback with (AE,CWR,ECE) = (1,1,1) then receives a SYN/ACK with the currently reserved combination (AE,CWR,ECE) = (1,0,1) but it does not have logic specific to such a combination, the Client MUST enable AccECN mode as if the SYN/ACK onfirmed that the Server supported AccECN and as if it fed back that the IP-ECN field on the SYN had arrived unchanged.
--- include/net/tcp_ecn.h | 44 ++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-)
include/net/tcp_ecn.h +31 -13
@@ -473,6 +473,26 @@ static inline u8 tcp_accecn_option_init(const struct sk_buff *skb,return TCP_ACCECN_OPT_COUNTER_SEEN ; } +static inline void tcp_ecn_rcv_synack_accecn (struct sock *sk, +const struct sk_buff *skb,u8 dsf) +{ +struct tcp_sock *tp =tcp_sk (sk); + +tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); + tp->syn_ect_rcv = dsf &INET_ECN_MASK ; +/* Demand Accurate ECN option in response to the SYN on the SYN/ACK +* and the TCP server will try to send one more packet with an AccECN +* Option at a later point during the connection. +*/ +if (tp->rx_opt.accecn && + tp->saw_accecn_opt <TCP_ACCECN_OPT_COUNTER_SEEN ) { +u8 saw_opt =tcp_accecn_option_init (skb, tp->rx_opt.accecn); + +tcp_accecn_saw_opt_fail_recv (tp, saw_opt); + tp->accecn_opt_demand =2 ; + } +} +/* See Table 2 of the AccECN draft */ static inline void tcp_ecn_rcv_synack (struct sock *sk,const struct sk_buff *skb,const struct tcphdr *th,u8 ip_dsfield)
@@ -495,13 +515,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skbtcp_ecn_mode_set (tp,TCP_ECN_DISABLED );break ;case 0x1 : -case 0x5 :/* +========+========+============+=============+ * | A | B | SYN/ACK | Feedback | * | | | B->A | Mode of A | * | | | AE CWR ECE | | * +========+========+============+=============+ -* | AccECN | Nonce | 1 0 1 | (Reserved) | * | AccECN | ECN | 0 0 1 | Classic ECN | * | Nonce | AccECN | 0 0 1 | Classic ECN | * | ECN | AccECN | 0 0 1 | Classic ECN |
@@ -509,20 +527,20 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb*/ if (tcp_ca_no_fallback_rfc3168 (sk))tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); -else if (tcp_ecn_mode_pending (tp)) -/* Downgrade from AccECN, or requested initially */ +else tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 );break ; -default : -tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); - tp->syn_ect_rcv = ip_dsfield &INET_ECN_MASK ; -if (tp->rx_opt.accecn && - tp->saw_accecn_opt <TCP_ACCECN_OPT_COUNTER_SEEN ) { -u8 saw_opt =tcp_accecn_option_init (skb, tp->rx_opt.accecn); - -tcp_accecn_saw_opt_fail_recv (tp, saw_opt); - tp->accecn_opt_demand =2 ; +case 0x5 : +if (tcp_ecn_mode_pending (tp)) { +tcp_ecn_rcv_synack_accecn (sk, skb, ip_dsfield); +if (INET_ECN_is_ce (ip_dsfield)) { + tp->received_ce++; + tp->received_ce_pending++; + } } +break ; +default : +tcp_ecn_rcv_synack_accecn (sk, skb, ip_dsfield);if (INET_ECN_is_ce (ip_dsfield) &&tcp_accecn_validate_syn_feedback (sk, ace, tp->syn_ect_snt)) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 390/781 PCI: Add preceding capability position support in PCI_FIND_NEXT_*_CAP macros
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiang Yu <[email protected]>
[ Upstream commit a2582e05e39adf9ab82a02561cd6f70738540ae0 ]
Add support for finding the preceding capability position in PCI capability list by extending the capability finding macros with an additional parameter. This functionality is essential for modifying PCI capability list, as it provides the necessary information to update the "next" pointer of the predecessor capability when removing entries.
Modify two macros to accept a new 'prev_ptr' parameter:
- PCI_FIND_NEXT_CAP - Now accepts 'prev_ptr' parameter for standard capabilities
- PCI_FIND_NEXT_EXT_CAP - Now accepts 'prev_ptr' parameter for extended capabilities
When a capability is found, these macros:
- Store the position of the preceding capability in *prev_ptr (if prev_ptr != NULL)
- Maintain all existing functionality when prev_ptr is NULL
Update current callers to accommodate this API change by passing NULL to 'prev_ptr' argument if they do not care about the preceding capability position.
No functional changes to driver behavior result from this commit as it maintains the existing capability finding functionality while adding the infrastructure for future capability removal operations.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- drivers/pci/controller/cadence/pcie-cadence.c | 4 ++-- .../pci/controller/dwc/pcie-designware-ep.c | 2 +- drivers/pci/controller/dwc/pcie-designware.c | 6 ++--- drivers/pci/pci.c | 8 +++---- drivers/pci/pci.h | 23 +++++++++++++++---- 5 files changed, 29 insertions(+), 14 deletions(-)
drivers/pci/controller/cadence/pcie-cadence.c +2 -2
@@ -13,13 +13,13 @@u8 cdns_pcie_find_capability (struct cdns_pcie *pcie,u8 cap) {return PCI_FIND_NEXT_CAP (cdns_pcie_read_cfg,PCI_CAPABILITY_LIST , - cap, pcie); + cap,NULL , pcie); }EXPORT_SYMBOL_GPL (cdns_pcie_find_capability);u16 cdns_pcie_find_ext_capability (struct cdns_pcie *pcie,u8 cap) { -return PCI_FIND_NEXT_EXT_CAP (cdns_pcie_read_cfg,0 , cap, pcie); +return PCI_FIND_NEXT_EXT_CAP (cdns_pcie_read_cfg,0 , cap,NULL , pcie); }EXPORT_SYMBOL_GPL (cdns_pcie_find_ext_capability);
drivers/pci/controller/dwc/pcie-designware-ep.c +1 -1
@@ -72,7 +72,7 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar);static u8 dw_pcie_ep_find_capability (struct dw_pcie_ep *ep,u8 func_no,u8 cap) {return PCI_FIND_NEXT_CAP (dw_pcie_ep_read_cfg,PCI_CAPABILITY_LIST , - cap, ep, func_no); + cap,NULL , ep, func_no); }/**
drivers/pci/controller/dwc/pcie-designware.c +3 -3
@@ -226,13 +226,13 @@ void dw_pcie_version_detect(struct dw_pcie *pci)u8 dw_pcie_find_capability (struct dw_pcie *pci,u8 cap) {return PCI_FIND_NEXT_CAP (dw_pcie_read_cfg,PCI_CAPABILITY_LIST , cap, - pci); +NULL , pci); }EXPORT_SYMBOL_GPL (dw_pcie_find_capability);u16 dw_pcie_find_ext_capability (struct dw_pcie *pci,u8 cap) { -return PCI_FIND_NEXT_EXT_CAP (dw_pcie_read_cfg,0 , cap, pci); +return PCI_FIND_NEXT_EXT_CAP (dw_pcie_read_cfg,0 , cap,NULL , pci); }EXPORT_SYMBOL_GPL (dw_pcie_find_ext_capability);
@@ -246,7 +246,7 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id,return 0 ;while ((vsec =PCI_FIND_NEXT_EXT_CAP (dw_pcie_read_cfg, vsec, -PCI_EXT_CAP_ID_VNDR , pci))) { +PCI_EXT_CAP_ID_VNDR ,NULL , pci))) { header =dw_pcie_readl_dbi (pci, vsec +PCI_VNDR_HEADER );if (PCI_VNDR_HEADER_ID (header) == vsec_id)return vsec;
drivers/pci/pci.c +4 -4
@@ -426,7 +426,7 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,static u8 __pci_find_next_cap (struct pci_bus *bus,unsigned int devfn,u8 pos,int cap) { -return PCI_FIND_NEXT_CAP (pci_bus_read_config, pos, cap, bus, devfn); +return PCI_FIND_NEXT_CAP (pci_bus_read_config, pos, cap,NULL , bus, devfn); }u8 pci_find_next_capability (struct pci_dev *dev,u8 pos,int cap)
@@ -531,7 +531,7 @@ u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 start, int cap)return 0 ;return PCI_FIND_NEXT_EXT_CAP (pci_bus_read_config, start, cap, - dev->bus, dev->devfn); +NULL , dev->bus, dev->devfn); }EXPORT_SYMBOL_GPL (pci_find_next_ext_capability);
@@ -600,7 +600,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) mask =HT_5BIT_CAP_MASK ; pos =PCI_FIND_NEXT_CAP (pci_bus_read_config, pos, -PCI_CAP_ID_HT , dev->bus, dev->devfn); +PCI_CAP_ID_HT ,NULL , dev->bus, dev->devfn);while (pos) { rc =pci_read_config_byte (dev, pos +3 , &cap);if (rc !=PCIBIOS_SUCCESSFUL )
@@ -611,7 +611,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) pos =PCI_FIND_NEXT_CAP (pci_bus_read_config, pos +PCI_CAP_LIST_NEXT , -PCI_CAP_ID_HT , dev->bus, +PCI_CAP_ID_HT ,NULL , dev->bus, dev->devfn); }
drivers/pci/pci.h +19 -4
@@ -106,17 +106,21 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);* @read_cfg: Function pointer for reading PCI config space * @start: Starting position to begin search * @cap: Capability ID to find +* @prev_ptr: Pointer to store position of preceding capability (optional) * @args: Arguments to pass to read_cfg function * -* Search the capability list in PCI config space to find @cap. +* Search the capability list in PCI config space to find @cap. If +* found, update *prev_ptr with the position of the preceding capability +* (if prev_ptr != NULL) * Implements TTL (time-to-live) protection against infinite loops. * * Return: Position of the capability if found, 0 otherwise. */ -#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...) \ +#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, prev_ptr, args...) \ ({ \ int __ttl = PCI_FIND_CAP_TTL; \ - u8 __id, __found_pos = 0; \ + u8 __id, __found_pos = 0; \ + u8 __prev_pos = (start); \ u8 __pos = (start); \ u16 __ent; \ \
@@ -135,9 +139,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); \if (__id == (cap)) { \ __found_pos = __pos; \ +if (prev_ptr !=NULL ) \ + *(u8 *)prev_ptr = __prev_pos; \break ; \ } \ \ + __prev_pos = __pos; \ __pos =FIELD_GET (PCI_CAP_LIST_NEXT_MASK , __ent); \ } \ __found_pos; \
@@ -149,21 +156,26 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);* @read_cfg: Function pointer for reading PCI config space * @start: Starting position to begin search (0 for initial search) * @cap: Extended capability ID to find +* @prev_ptr: Pointer to store position of preceding capability (optional) * @args: Arguments to pass to read_cfg function * * Search the extended capability list in PCI config space to find @cap. +* If found, update *prev_ptr with the position of the preceding capability +* (if prev_ptr != NULL) * Implements TTL protection against infinite loops using a calculated * maximum search count. * * Return: Position of the capability if found, 0 otherwise. */ -#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, args...) \ +#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, prev_ptr, args...) \ ({ \ u16 __pos = (start) ?: PCI_CFG_SPACE_SIZE; \ u16 __found_pos = 0; \ + u16 __prev_pos; \ int __ttl, __ret; \ u32 __header; \ \ + __prev_pos = __pos; \ __ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; \ while (__ttl-- > 0 && __pos >= PCI_CFG_SPACE_SIZE) { \ __ret = read_cfg##_dword(args, __pos, &__header); \
@@ -175,9 +187,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); \if (PCI_EXT_CAP_ID (__header) == (cap ) && __pos != start) {\ __found_pos = __pos; \ +if (prev_ptr !=NULL ) \ + *(u16 *)prev_ptr = __prev_pos; \break ; \ } \ \ + __prev_pos = __pos; \ __pos =PCI_EXT_CAP_NEXT (__header); \ } \ __found_pos; \
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 391/781 PCI: dwc: Add new APIs to remove standard and extended Capability
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiang Yu <[email protected]>
[ Upstream commit 0183562f1e824c0ca6c918309a0978e9a269af3e ]
On some platforms, certain PCIe Capabilities may be present in hardware but are not fully implemented as defined in PCIe spec. These incomplete capabilities should be hidden from the PCI framework to prevent unexpected behavior.
Introduce two APIs to remove a specific PCIe Capability and Extended Capability by updating the previous capability's next offset field to skip over the unwanted capability. These APIs allow RC drivers to easily hide unsupported or partially implemented capabilities from software.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- drivers/pci/controller/dwc/pcie-designware.c | 53 ++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 2 + 2 files changed, 55 insertions(+)
drivers/pci/controller/dwc/pcie-designware.c +53 -0
@@ -236,6 +236,59 @@ u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) }EXPORT_SYMBOL_GPL (dw_pcie_find_ext_capability); +void dw_pcie_remove_capability (struct dw_pcie *pci,u8 cap) +{ +u8 cap_pos, pre_pos, next_pos; +u16 reg; + + cap_pos =PCI_FIND_NEXT_CAP (dw_pcie_read_cfg,PCI_CAPABILITY_LIST , cap, + &pre_pos, pci); +if (!cap_pos) +return ; + + reg =dw_pcie_readw_dbi (pci, cap_pos); + next_pos = (reg &0xff00 ) >>8 ; + +dw_pcie_dbi_ro_wr_en (pci); +if (pre_pos ==PCI_CAPABILITY_LIST ) +dw_pcie_writeb_dbi (pci,PCI_CAPABILITY_LIST , next_pos); +else +dw_pcie_writeb_dbi (pci, pre_pos +1 , next_pos); +dw_pcie_dbi_ro_wr_dis (pci); +} +EXPORT_SYMBOL_GPL (dw_pcie_remove_capability); + +void dw_pcie_remove_ext_capability (struct dw_pcie *pci,u8 cap) +{ +int cap_pos, next_pos, pre_pos; +u32 pre_header, header; + + cap_pos =PCI_FIND_NEXT_EXT_CAP (dw_pcie_read_cfg,0 , cap, &pre_pos, pci); +if (!cap_pos) +return ; + + header =dw_pcie_readl_dbi (pci, cap_pos); +/* +* If the first cap at offset PCI_CFG_SPACE_SIZE is removed, +* only set it's capid to zero as it cannot be skipped. +*/ +if (cap_pos ==PCI_CFG_SPACE_SIZE ) { +dw_pcie_dbi_ro_wr_en (pci); +dw_pcie_writel_dbi (pci, cap_pos, header &0xffff0000 ); +dw_pcie_dbi_ro_wr_dis (pci); +return ; + } + + pre_header =dw_pcie_readl_dbi (pci, pre_pos); + next_pos =PCI_EXT_CAP_NEXT (header); + +dw_pcie_dbi_ro_wr_en (pci); +dw_pcie_writel_dbi (pci, pre_pos, + (pre_header &0xfffff ) | (next_pos <<20 )); +dw_pcie_dbi_ro_wr_dis (pci); +} +EXPORT_SYMBOL_GPL (dw_pcie_remove_ext_capability); +static u16 __dw_pcie_find_vsec_capability (struct dw_pcie *pci,u16 vendor_id,u16 vsec_id) {
drivers/pci/controller/dwc/pcie-designware.h +2 -0
@@ -562,6 +562,8 @@ void dw_pcie_version_detect(struct dw_pcie *pci);u8 dw_pcie_find_capability (struct dw_pcie *pci,u8 cap);u16 dw_pcie_find_ext_capability (struct dw_pcie *pci,u8 cap); +void dw_pcie_remove_capability (struct dw_pcie *pci,u8 cap); +void dw_pcie_remove_ext_capability (struct dw_pcie *pci,u8 cap);u16 dw_pcie_find_rasdes_capability (struct dw_pcie *pci);u16 dw_pcie_find_ptm_capability (struct dw_pcie *pci);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 392/781 PCI: dwc: ep: Cache MSI outbound iATU mapping
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit 8719c64e76bf258cc8f44109740c854f2e2ead2e ]
dw_pcie_ep_raise_msi_irq() currently programs an outbound iATU window for the MSI target address on every interrupt and tears it down again via dw_pcie_ep_unmap_addr().
On systems that heavily use the AXI bridge interface (for example when the integrated eDMA engine is active), this means the outbound iATU registers are updated while traffic is in flight. The DesignWare endpoint databook 5.40a - "3.10.6.1 iATU Outbound Programming Overview" warns that updating iATU registers in this situation is not supported, and the behavior is undefined.
Under high MSI and eDMA load this pattern results in occasional bogus outbound transactions and IOMMU faults, on the RC side, such as:
ipmmu-vmsa eed40000.iommu: Unhandled fault: status 0x00001502 iova 0xfe000000
followed by the system becoming unresponsive. This is the actual output observed on Renesas R-Car S4, with its ipmmu_hc used with PCIe ch0.
There is no need to reprogram the iATU region used for MSI on every interrupt. The host-provided MSI address is stable while MSI is enabled, and the endpoint driver already dedicates a scratch buffer for MSI generation.
Cache the aligned MSI address and map size, program the outbound iATU once, and keep the window enabled. Subsequent interrupts only perform a write to the MSI scratch buffer, avoiding dynamic iATU reprogramming in the hot path and fixing the lockups seen under load.
dw_pcie_ep_raise_msix_irq() is not modified, as each vector can have a different msg_addr, and because the msg_addr is allowed to be changed while the vector is masked. Neither problem is easy to solve with the current design. Instead, the plan is for the DWC vendor drivers to transition to dw_pcie_ep_raise_msix_irq_doorbell(), which does not rely on the iATU.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- .../pci/controller/dwc/pcie-designware-ep.c | 48 ++++++++++++++++--- drivers/pci/controller/dwc/pcie-designware.h | 5 ++ 2 files changed, 47 insertions(+), 6 deletions(-)
drivers/pci/controller/dwc/pcie-designware-ep.c +42 -6
@@ -601,6 +601,16 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)struct dw_pcie_ep *ep =epc_get_drvdata (epc);struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +/* +* Tear down the dedicated outbound window used for MSI +* generation. This avoids leaking an iATU window across +* endpoint stop/start cycles. +*/ +if (ep->msi_iatu_mapped) { +dw_pcie_ep_unmap_addr (epc,0 ,0 , ep->msi_mem_phys); + ep->msi_iatu_mapped = false; + } +dw_pcie_stop_link (pci); }
@@ -702,14 +712,37 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, msg_addr = ((u64 )msg_addr_upper) <<32 | msg_addr_lower; msg_addr =dw_pcie_ep_align_addr (epc, msg_addr, &map_size, &offset); - ret =dw_pcie_ep_map_addr (epc, func_no,0 , ep->msi_mem_phys, msg_addr, - map_size); -if (ret) -return ret; -writel (msg_data | (interrupt_num -1 ), ep->msi_mem + offset); +/* +* Program the outbound iATU once and keep it enabled. +* +* The spec warns that updating iATU registers while there are +* operations in flight on the AXI bridge interface is not +* supported, so we avoid reprogramming the region on every MSI, +* specifically unmapping immediately after writel(). +*/ +if (!ep->msi_iatu_mapped) { + ret =dw_pcie_ep_map_addr (epc, func_no,0 , + ep->msi_mem_phys, msg_addr, + map_size); +if (ret) +return ret; + + ep->msi_iatu_mapped = true; + ep->msi_msg_addr = msg_addr; + ep->msi_map_size = map_size; + }else if (WARN_ON_ONCE (ep->msi_msg_addr != msg_addr || + ep->msi_map_size != map_size)) { +/* +* The host changed the MSI target address or the required +* mapping size changed. Reprogramming the iATU at runtime is +* unsafe on this controller, so bail out instead of trying to +* update the existing region. +*/ +return -EINVAL ; + } -dw_pcie_ep_unmap_addr (epc, func_no,0 , ep->msi_mem_phys); +writel (msg_data | (interrupt_num -1 ), ep->msi_mem + offset);return 0 ; }
@@ -1087,6 +1120,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)struct device *dev = pci->dev;INIT_LIST_HEAD (&ep->func_list); + ep->msi_iatu_mapped = false; + ep->msi_msg_addr =0 ; + ep->msi_map_size =0 ; epc =devm_pci_epc_create (dev, &epc_ops);if (IS_ERR (epc)) {
drivers/pci/controller/dwc/pcie-designware.h +5 -0
@@ -479,6 +479,11 @@ struct dw_pcie_ep {void __iomem *msi_mem;phys_addr_t msi_mem_phys;struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS ]; + +/* MSI outbound iATU state */ +bool msi_iatu_mapped; +u64 msi_msg_addr; +size_t msi_map_size; };struct dw_pcie_ops {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 393/781 PCI: dwc: Remove duplicate dw_pcie_ep_hide_ext_capability() function
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qiang Yu <[email protected]>
[ Upstream commit 86291f774fe8524178446cb2c792939640b4970c ]
Remove dw_pcie_ep_hide_ext_capability() and replace its usage with dw_pcie_remove_ext_capability(). Both functions serve the same purpose of hiding PCIe extended capabilities, but dw_pcie_remove_ext_capability() provides a cleaner API that doesn't require the caller to specify the previous capability ID.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- .../pci/controller/dwc/pcie-designware-ep.c | 39 ------------------- drivers/pci/controller/dwc/pcie-designware.h | 7 ---- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 4 +- 3 files changed, 1 insertion(+), 49 deletions(-)
drivers/pci/controller/dwc/pcie-designware-ep.c +0 -39
@@ -75,45 +75,6 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) cap, NULL, ep, func_no); } -/** -* dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list -* @pci: DWC PCI device -* @prev_cap: Capability preceding the capability that should be hidden -* @cap: Capability that should be hidden -* -* Return: 0 if success, errno otherwise. -*/ -int dw_pcie_ep_hide_ext_capability (struct dw_pcie *pci,u8 prev_cap,u8 cap) -{ -u16 prev_cap_offset, cap_offset; -u32 prev_cap_header, cap_header; - - prev_cap_offset =dw_pcie_find_ext_capability (pci, prev_cap); -if (!prev_cap_offset) -return -EINVAL ; - - prev_cap_header =dw_pcie_readl_dbi (pci, prev_cap_offset); - cap_offset =PCI_EXT_CAP_NEXT (prev_cap_header); - cap_header =dw_pcie_readl_dbi (pci, cap_offset); - -/* cap must immediately follow prev_cap. */ -if (PCI_EXT_CAP_ID (cap_header) != cap) -return -EINVAL ; - -/* Clear next ptr. */ - prev_cap_header &= ~GENMASK (31 ,20 ); - -/* Set next ptr to next ptr of cap. */ - prev_cap_header |= cap_header &GENMASK (31 ,20 ); - -dw_pcie_dbi_ro_wr_en (pci); -dw_pcie_writel_dbi (pci, prev_cap_offset, prev_cap_header); -dw_pcie_dbi_ro_wr_dis (pci); - -return 0 ; -} -EXPORT_SYMBOL_GPL (dw_pcie_ep_hide_ext_capability); -static int dw_pcie_ep_write_header (struct pci_epc *epc,u8 func_no,u8 vfunc_no,struct pci_epf_header *hdr) {
drivers/pci/controller/dwc/pcie-designware.h +0 -7
@@ -906,7 +906,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,int dw_pcie_ep_raise_msix_irq_doorbell (struct dw_pcie_ep *ep,u8 func_no,u16 interrupt_num);void dw_pcie_ep_reset_bar (struct dw_pcie *pci,enum pci_barno bar); -int dw_pcie_ep_hide_ext_capability (struct dw_pcie *pci,u8 prev_cap,u8 cap);struct dw_pcie_ep_func *dw_pcie_ep_get_func_from_ep (struct dw_pcie_ep *ep,u8 func_no);#else
@@ -964,12 +963,6 @@ static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { } -static inline int dw_pcie_ep_hide_ext_capability (struct dw_pcie *pci, -u8 prev_cap,u8 cap) -{ -return 0 ; -} -static inline struct dw_pcie_ep_func *dw_pcie_ep_get_func_from_ep (struct dw_pcie_ep *ep,u8 func_no) {
drivers/pci/controller/dwc/pcie-dw-rockchip.c +1 -3
@@ -327,9 +327,7 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep)if (!of_device_is_compatible (dev->of_node,"rockchip,rk3588-pcie-ep" ))return ; -if (dw_pcie_ep_hide_ext_capability (pci,PCI_EXT_CAP_ID_SECPCI , -PCI_EXT_CAP_ID_ATS )) -dev_err (dev,"failed to hide ATS capability\n" ); +dw_pcie_remove_ext_capability (pci,PCI_EXT_CAP_ID_ATS ); }static void rockchip_pcie_ep_init (struct dw_pcie_ep *ep)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 394/781 PCI: endpoint: Add dynamic_inbound_mapping EPC feature
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit 06a81c5940e46cc7bddee28f16bdd29a12a76344 ]
Introduce a new EPC feature bit (dynamic_inbound_mapping) that indicates whether an Endpoint Controller can update the inbound address translation for a BAR without requiring the EPF driver to clear/reset the BAR first.
Endpoint Function drivers (e.g. vNTB) can use this information to decide whether it really is safe to call pci_epc_set_bar() multiple times to update inbound mappings for the BAR.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- include/linux/pci-epc.h | 5 +++++ 1 file changed, 5 insertions(+)
include/linux/pci-epc.h +5 -0
@@ -223,6 +223,10 @@ struct pci_epc_bar_desc {/** * struct pci_epc_features - features supported by a EPC device per function * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up +* @dynamic_inbound_mapping: indicate if the EPC device supports updating +* inbound mappings for an already configured BAR +* (i.e. allow calling pci_epc_set_bar() again +* without first calling pci_epc_clear_bar()) * @msi_capable: indicate if the endpoint function has MSI capability * @msix_capable: indicate if the endpoint function has MSI-X capability * @intx_capable: indicate if the endpoint can raise INTx interrupts
@@ -231,6 +235,7 @@ struct pci_epc_bar_desc {*/ struct pci_epc_features {unsigned int linkup_notifier :1 ; +unsigned int dynamic_inbound_mapping :1 ;unsigned int msi_capable :1 ;unsigned int msix_capable :1 ;unsigned int intx_capable :1 ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 395/781 PCI: endpoint: Add BAR subrange mapping support
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit 31fb95400451040050361e22ff480476964280f0 ]
Some endpoint platforms have only a small number of usable BARs. At the same time, EPF drivers (e.g. vNTB) may need multiple independent inbound regions (control/scratchpad, one or more memory windows, and optionally MSI or other feature-related regions). Subrange mapping allows these to share a single BAR without consuming additional BARs that may not be available, or forcing a fragile layout by aggressively packing into a single contiguous memory range.
Extend the PCI endpoint core to support mapping subranges within a BAR. Add an optional 'submap' field in struct pci_epf_bar so an endpoint function driver can request inbound mappings that fully cover the BAR.
Introduce a new EPC feature bit, subrange_mapping, and reject submap requests from pci_epc_set_bar() unless the controller advertises both subrange_mapping and dynamic_inbound_mapping features.
The submap array describes the complete BAR layout (no overlaps and no gaps are allowed to avoid exposing untranslated address ranges). This provides the generic infrastructure needed to map multiple logical regions into a single BAR at different offsets, without assuming a controller-specific inbound address translation mechanism.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- drivers/pci/endpoint/pci-epc-core.c | 8 ++++++++ include/linux/pci-epc.h | 4 ++++ include/linux/pci-epf.h | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+)
drivers/pci/endpoint/pci-epc-core.c +8 -0
@@ -596,6 +596,14 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,if (!epc_features)return -EINVAL ; +if (epf_bar->num_submap && !epf_bar->submap) +return -EINVAL ; + +if (epf_bar->num_submap && + !(epc_features->dynamic_inbound_mapping && + epc_features->subrange_mapping)) +return -EINVAL ; +if (epc_features->bar[bar].type ==BAR_RESIZABLE && (epf_bar->size <SZ_1M || (u64 )epf_bar->size > (SZ_128G *1024 )))return -EINVAL ;
include/linux/pci-epc.h +4 -0
@@ -227,6 +227,9 @@ struct pci_epc_bar_desc {* inbound mappings for an already configured BAR * (i.e. allow calling pci_epc_set_bar() again * without first calling pci_epc_clear_bar()) +* @subrange_mapping: indicate if the EPC device can map inbound subranges for a +* BAR. This feature depends on @dynamic_inbound_mapping +* feature. * @msi_capable: indicate if the endpoint function has MSI capability * @msix_capable: indicate if the endpoint function has MSI-X capability * @intx_capable: indicate if the endpoint can raise INTx interrupts
@@ -236,6 +239,7 @@ struct pci_epc_bar_desc {struct pci_epc_features {unsigned int linkup_notifier :1 ;unsigned int dynamic_inbound_mapping :1 ; +unsigned int subrange_mapping :1 ;unsigned int msi_capable :1 ;unsigned int msix_capable :1 ;unsigned int intx_capable :1 ;
include/linux/pci-epf.h +23 -0
@@ -110,6 +110,22 @@ struct pci_epf_driver {#define to_pci_epf_driver (drv) container_of_const((drv), struct pci_epf_driver, driver) +/** +* struct pci_epf_bar_submap - BAR subrange for inbound mapping +* @phys_addr: target physical/DMA address for this subrange +* @size: the size of the subrange to be mapped +* +* When pci_epf_bar.num_submap is >0, pci_epf_bar.submap describes the +* complete BAR layout. This allows an EPC driver to program multiple +* inbound translation windows for a single BAR when supported by the +* controller. The array order defines the BAR layout (submap[0] at offset +* 0, and each immediately follows the previous one). +*/ +struct pci_epf_bar_submap { +dma_addr_t phys_addr; +size_t size; +}; +/** * struct pci_epf_bar - represents the BAR of EPF device * @phys_addr: physical address that should be mapped to the BAR
@@ -119,6 +135,9 @@ struct pci_epf_driver {* requirement * @barno: BAR number * @flags: flags that are set for the BAR +* @num_submap: number of entries in @submap +* @submap: array of subrange descriptors allocated by the caller. See +* struct pci_epf_bar_submap for the semantics in detail. */ struct pci_epf_bar {dma_addr_t phys_addr;
@@ -127,6 +146,10 @@ struct pci_epf_bar {size_t mem_size;enum pci_barno barno;int flags; + +/* Optional sub-range mapping */ +unsigned int num_submap; +struct pci_epf_bar_submap *submap; };/**
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 396/781 PCI: dwc: Advertise dynamic inbound mapping support
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit c0f1506f63546308e894469ceb0f1fadbdf9d2f9 ]
The DesignWare EP core has supported updating the inbound iATU mapping for an already configured BAR (i.e. allowing pci_epc_set_bar() to be called again without a prior pci_epc_clear_bar()) since commit 4284c88fff0e ("PCI: designware-ep: Allow pci_epc_set_bar() update inbound map address").
Now that this capability is exposed via the dynamic_inbound_mapping EPC feature bit, set it for DWC-based EP glue drivers using a common initializer macro to avoid duplicating the same flag in each driver.
Note that pci-layerscape-ep.c is untouched. It currently constructs the feature struct dynamically in ls_pcie_ep_init(). Once converted to a static feature definition, it will use DWC_EPC_COMMON_FEATURES as well.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- drivers/pci/controller/dwc/pci-dra7xx.c | 1 + drivers/pci/controller/dwc/pci-imx6.c | 3 +++ drivers/pci/controller/dwc/pci-keystone.c | 1 + drivers/pci/controller/dwc/pcie-artpec6.c | 1 + drivers/pci/controller/dwc/pcie-designware-plat.c | 1 + drivers/pci/controller/dwc/pcie-designware.h | 3 +++ drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 ++ drivers/pci/controller/dwc/pcie-keembay.c | 1 + drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 + drivers/pci/controller/dwc/pcie-rcar-gen4.c | 1 + drivers/pci/controller/dwc/pcie-stm32-ep.c | 1 + drivers/pci/controller/dwc/pcie-tegra194.c | 1 + drivers/pci/controller/dwc/pcie-uniphier-ep.c | 2 ++ 13 files changed, 19 insertions(+)
drivers/pci/controller/dwc/pci-dra7xx.c +1 -0
@@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features dra7xx_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .linkup_notifier = true, .msi_capable = true, };
drivers/pci/controller/dwc/pci-imx6.c +3 -0
@@ -1387,6 +1387,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features imx8m_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .bar[BAR_1 ] = { .type =BAR_RESERVED , }, .bar[BAR_3 ] = { .type =BAR_RESERVED , },
@@ -1396,6 +1397,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = { };static const struct pci_epc_features imx8q_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .bar[BAR_1 ] = { .type =BAR_RESERVED , }, .bar[BAR_3 ] = { .type =BAR_RESERVED , },
@@ -1416,6 +1418,7 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {* BAR5 | Enable | 32-bit | 64 KB | Programmable Size */ static const struct pci_epc_features imx95_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .bar[BAR_1 ] = { .type =BAR_FIXED , .fixed_size =SZ_64K , }, .align =SZ_4K ,
drivers/pci/controller/dwc/pci-keystone.c +1 -0
@@ -930,6 +930,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features ks_pcie_am654_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .msix_capable = true, .bar[BAR_0 ] = { .type =BAR_RESERVED , },
drivers/pci/controller/dwc/pcie-artpec6.c +1 -0
@@ -370,6 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features artpec6_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, };
drivers/pci/controller/dwc/pcie-designware-plat.c +1 -0
@@ -61,6 +61,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features dw_plat_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .msix_capable = true, };
drivers/pci/controller/dwc/pcie-designware.h +3 -0
@@ -305,6 +305,9 @@/* Default eDMA LLP memory size */ #define DMA_LLP_MEM_SIZE PAGE_SIZE +/* Common struct pci_epc_feature bits among DWC EP glue drivers */ +#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true +struct dw_pcie ;struct dw_pcie_rp ;struct dw_pcie_ep ;
drivers/pci/controller/dwc/pcie-dw-rockchip.c +2 -0
@@ -362,6 +362,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { +DWC_EPC_COMMON_FEATURES , .linkup_notifier = true, .msi_capable = true, .msix_capable = true,
@@ -382,6 +383,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {* BARs) would be overwritten, resulting in (all other BARs) no longer working. */ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { +DWC_EPC_COMMON_FEATURES , .linkup_notifier = true, .msi_capable = true, .msix_capable = true,
drivers/pci/controller/dwc/pcie-keembay.c +1 -0
@@ -309,6 +309,7 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features keembay_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .msix_capable = true, .bar[BAR_0 ] = { .only_64bit = true, },
drivers/pci/controller/dwc/pcie-qcom-ep.c +1 -0
@@ -820,6 +820,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) }static const struct pci_epc_features qcom_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .linkup_notifier = true, .msi_capable = true, .align =SZ_4K ,
drivers/pci/controller/dwc/pcie-rcar-gen4.c +1 -0
@@ -420,6 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features rcar_gen4_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .bar[BAR_1 ] = { .type =BAR_RESERVED , }, .bar[BAR_3 ] = { .type =BAR_RESERVED , },
drivers/pci/controller/dwc/pcie-stm32-ep.c +1 -0
@@ -70,6 +70,7 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features stm32_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .msi_capable = true, .align =SZ_64K , };
drivers/pci/controller/dwc/pcie-tegra194.c +1 -0
@@ -1988,6 +1988,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, }static const struct pci_epc_features tegra_pcie_epc_features = { +DWC_EPC_COMMON_FEATURES , .linkup_notifier = true, .msi_capable = true, .bar[BAR_0 ] = { .type =BAR_FIXED , .fixed_size =SZ_1M ,
drivers/pci/controller/dwc/pcie-uniphier-ep.c +2 -0
@@ -420,6 +420,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .init = uniphier_pcie_pro5_init_ep, .wait = NULL, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false,
@@ -438,6 +439,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = { .init = uniphier_pcie_nx1_init_ep, .wait = uniphier_pcie_nx1_wait_ep, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 397/781 PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit cc839bef7727043a66004bba563492957ca3e531 ]
Extend dw_pcie_ep_set_bar() to support inbound mappings for BAR subranges using Address Match Mode IB iATU when pci_epf_bar.num_submap is non-zero.
Rename the existing BAR-match helper into dw_pcie_ep_ib_atu_bar() and introduce dw_pcie_ep_ib_atu_addr() for Address Match Mode. When num_submap is non-zero, read the assigned BAR base address and program one inbound iATU window per subrange. Validate the submap array before programming:
- each subrange is aligned to pci->region_align
- subranges cover the whole BAR (no gaps and no overlaps)
Track Address Match Mode mappings and tear them down on clear_bar() and on set_bar() error paths to avoid leaving half-programmed state or untranslated BAR holes.
Advertise this capability by extending the common feature bit initializer macro (DWC_EPC_COMMON_FEATURES).
This enables multiple inbound windows within a single BAR, which is useful on platforms where usable BARs are scarce but EPFs need multiple inbound regions.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- .../pci/controller/dwc/pcie-designware-ep.c | 213 +++++++++++++++++- drivers/pci/controller/dwc/pcie-designware.h | 7 +- 2 files changed, 209 insertions(+), 11 deletions(-)
drivers/pci/controller/dwc/pcie-designware-ep.c +203 -10
@@ -100,9 +100,10 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,return 0 ; } -static int dw_pcie_ep_inbound_atu (struct dw_pcie_ep *ep,u8 func_no,int type, -dma_addr_t parent_bus_addr,enum pci_barno bar, -size_t size) +/* BAR Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_bar (struct dw_pcie_ep *ep,u8 func_no,int type, +dma_addr_t parent_bus_addr,enum pci_barno bar, +size_t size) {int ret;u32 free_win;
@@ -135,6 +136,179 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,return 0 ; } +static void dw_pcie_ep_clear_ib_maps (struct dw_pcie_ep *ep,enum pci_barno bar) +{ +struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +struct device *dev = pci->dev; +unsigned int i, num; +u32 atu_index; +u32 *indexes; + +/* Tear down the BAR Match Mode mapping, if any. */ +if (ep->bar_to_atu[bar]) { + atu_index = ep->bar_to_atu[bar] -1 ; +dw_pcie_disable_atu (pci,PCIE_ATU_REGION_DIR_IB , atu_index); +clear_bit (atu_index, ep->ib_window_map); + ep->bar_to_atu[bar] =0 ; + } + +/* Tear down all Address Match Mode mappings, if any. */ + indexes = ep->ib_atu_indexes[bar]; + num = ep->num_ib_atu_indexes[bar]; + ep->ib_atu_indexes[bar] =NULL ; + ep->num_ib_atu_indexes[bar] =0 ; +if (!indexes) +return ; +for (i =0 ; i < num; i++) { +dw_pcie_disable_atu (pci,PCIE_ATU_REGION_DIR_IB , indexes[i]); +clear_bit (indexes[i], ep->ib_window_map); + } +devm_kfree (dev, indexes); +} + +static u64 dw_pcie_ep_read_bar_assigned (struct dw_pcie_ep *ep,u8 func_no, +enum pci_barno bar,int flags) +{ +u32 reg =PCI_BASE_ADDRESS_0 + (4 * bar); +u32 lo, hi; +u64 addr; + + lo =dw_pcie_ep_readl_dbi (ep, func_no, reg); + +if (flags &PCI_BASE_ADDRESS_SPACE ) +return lo &PCI_BASE_ADDRESS_IO_MASK ; + + addr = lo &PCI_BASE_ADDRESS_MEM_MASK ; +if (!(flags &PCI_BASE_ADDRESS_MEM_TYPE_64 )) +return addr; + + hi =dw_pcie_ep_readl_dbi (ep, func_no, reg +4 ); +return addr | ((u64 )hi <<32 ); +} + +static int dw_pcie_ep_validate_submap (struct dw_pcie_ep *ep, +const struct pci_epf_bar_submap *submap, +unsigned int num_submap,size_t bar_size) +{ +struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +u32 align = pci->region_align; +size_t off =0 ; +unsigned int i; +size_t size; + +if (!align || !IS_ALIGNED (bar_size, align)) +return -EINVAL ; + +/* +* The submap array order defines the BAR layout (submap[0] starts +* at offset 0 and each entry immediately follows the previous +* one). Here, validate that it forms a strict, gapless +* decomposition of the BAR: +* - each entry has a non-zero size +* - sizes, implicit offsets and phys_addr are aligned to +* pci->region_align +* - each entry lies within the BAR range +* - the entries exactly cover the whole BAR +* +* Note: dw_pcie_prog_inbound_atu() also checks alignment for the +* PCI address and the target phys_addr, but validating up-front +* avoids partially programming iATU windows in vain. +*/ +for (i =0 ; i < num_submap; i++) { + size = submap[i].size; + +if (!size) +return -EINVAL ; + +if (!IS_ALIGNED (size, align) || !IS_ALIGNED (off, align)) +return -EINVAL ; + +if (!IS_ALIGNED (submap[i].phys_addr, align)) +return -EINVAL ; + +if (off > bar_size || size > bar_size - off) +return -EINVAL ; + + off += size; + } +if (off != bar_size) +return -EINVAL ; + +return 0 ; +} + +/* Address Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_addr (struct dw_pcie_ep *ep,u8 func_no,int type, +const struct pci_epf_bar *epf_bar) +{ +const struct pci_epf_bar_submap *submap = epf_bar->submap; +struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +enum pci_barno bar = epf_bar->barno; +struct device *dev = pci->dev; +u64 pci_addr, parent_bus_addr; +u64 size, base, off =0 ; +int free_win, ret; +unsigned int i; +u32 *indexes; + +if (!epf_bar->num_submap || !submap || !epf_bar->size) +return -EINVAL ; + + ret =dw_pcie_ep_validate_submap (ep, submap, epf_bar->num_submap, + epf_bar->size); +if (ret) +return ret; + + base =dw_pcie_ep_read_bar_assigned (ep, func_no, bar, epf_bar->flags); +if (!base) { +dev_err (dev, +"BAR%u not assigned, cannot set up sub-range mappings\n" , + bar); +return -EINVAL ; + } + + indexes =devm_kcalloc (dev, epf_bar->num_submap,sizeof (*indexes), +GFP_KERNEL ); +if (!indexes) +return -ENOMEM ; + + ep->ib_atu_indexes[bar] = indexes; + ep->num_ib_atu_indexes[bar] =0 ; + +for (i =0 ; i < epf_bar->num_submap; i++) { + size = submap[i].size; + parent_bus_addr = submap[i].phys_addr; + +if (off > (~0ULL ) - base) { + ret = -EINVAL ; + goto err; + } + + pci_addr = base + off; + off += size; + + free_win =find_first_zero_bit (ep->ib_window_map, + pci->num_ib_windows); +if (free_win >= pci->num_ib_windows) { + ret = -ENOSPC ; + goto err; + } + + ret =dw_pcie_prog_inbound_atu (pci, free_win, type, + parent_bus_addr, pci_addr, size); +if (ret) + goto err; + +set_bit (free_win, ep->ib_window_map); + indexes[i] = free_win; + ep->num_ib_atu_indexes[bar] = i +1 ; + } +return 0 ; +err: +dw_pcie_ep_clear_ib_maps (ep, bar); +return ret; +} +static int dw_pcie_ep_outbound_atu (struct dw_pcie_ep *ep,struct dw_pcie_ob_atu_cfg *atu) {
@@ -165,17 +339,15 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,struct dw_pcie_ep *ep =epc_get_drvdata (epc);struct dw_pcie *pci =to_dw_pcie_from_ep (ep);enum pci_barno bar = epf_bar->barno; -u32 atu_index = ep->bar_to_atu[bar] -1 ; -if (!ep->bar_to_atu[bar]) +if (!ep->epf_bar[bar])return ;__dw_pcie_ep_reset_bar (pci, func_no, bar, epf_bar->flags); -dw_pcie_disable_atu (pci,PCIE_ATU_REGION_DIR_IB , atu_index); -clear_bit (atu_index, ep->ib_window_map); +dw_pcie_ep_clear_ib_maps (ep, bar); + ep->epf_bar[bar] =NULL ; - ep->bar_to_atu[bar] =0 ; } static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci,
@@ -331,11 +503,28 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, ep->epf_bar[bar]->flags != flags)return -EINVAL ; +/* +* When dynamically changing a BAR, tear down any existing +* mappings before re-programming. +*/ +if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap) +dw_pcie_ep_clear_ib_maps (ep, bar); +/* * When dynamically changing a BAR, skip writing the BAR reg, as * that would clear the BAR's PCI address assigned by the host. */ goto config_atu; + }else { +/* +* Subrange mapping is an update-only operation. The BAR +* must have been configured once without submaps so that +* subsequent set_bar() calls can update inbound mappings +* without touching the BAR register (and clobbering the +* host-assigned address). +*/ +if (epf_bar->num_submap) +return -EINVAL ; } bar_type =dw_pcie_ep_get_bar_type (ep, bar);
@@ -369,8 +558,12 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,else type =PCIE_ATU_TYPE_IO ; - ret =dw_pcie_ep_inbound_atu (ep, func_no, type, epf_bar->phys_addr, bar, - size); +if (epf_bar->num_submap) + ret =dw_pcie_ep_ib_atu_addr (ep, func_no, type, epf_bar); +else + ret =dw_pcie_ep_ib_atu_bar (ep, func_no, type, + epf_bar->phys_addr, bar, size); +if (ret)return ret;
drivers/pci/controller/dwc/pcie-designware.h +6 -1
@@ -306,7 +306,8 @@#define DMA_LLP_MEM_SIZE PAGE_SIZE/* Common struct pci_epc_feature bits among DWC EP glue drivers */ -#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true +#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \ + .subrange_mapping = truestruct dw_pcie ;struct dw_pcie_rp ;
@@ -483,6 +484,10 @@ struct dw_pcie_ep {phys_addr_t msi_mem_phys;struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS ]; +/* Only for Address Match Mode inbound iATU */ +u32 *ib_atu_indexes[PCI_STD_NUM_BARS ]; +unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS ]; +/* MSI outbound iATU state */ bool msi_iatu_mapped;u64 msi_msg_addr;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 398/781 PCI: dwc: ep: Fix resizable BAR support for multi-PF configurations
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aksh Garg <[email protected]>
[ Upstream commit 43d67ec26b329f8aea34ba9dff23d69b84a8e564 ]
The resizable BAR support added by the commit 3a3d4cabe681 ("PCI: dwc: ep:
Allow EPF drivers to configure the size of Resizable BARs") incorrectly configures the resizable BARs only for the first Physical Function (PF0) in EP mode.
The resizable BAR configuration functions use generic dw_pcie_*_dbi() operations instead of physical function specific dw_pcie_ep_*_dbi() operations. This causes resizable BAR configuration to always target PF0 regardless of the requested function number.
Additionally, dw_pcie_ep_init_non_sticky_registers() only initializes resizable BAR registers for PF0, leaving other PFs unconfigured during the execution of this function.
Fix this by using physical function specific configuration space access operations throughout the resizable BAR code path and initializing registers for all the physical functions that support resizable BARs.
Stable-dep-of: 72cb5ed2a5c6 ("PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support")
--- .../pci/controller/dwc/pcie-designware-ep.c | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-)
drivers/pci/controller/dwc/pcie-designware-ep.c +32 -16
@@ -75,6 +75,13 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) cap, NULL, ep, func_no); } +static u16 dw_pcie_ep_find_ext_capability (struct dw_pcie_ep *ep, +u8 func_no,u8 cap) +{ +return PCI_FIND_NEXT_EXT_CAP (dw_pcie_ep_read_cfg,0 , + cap,NULL , ep, func_no); +} +static int dw_pcie_ep_write_header (struct pci_epc *epc,u8 func_no,u8 vfunc_no,struct pci_epf_header *hdr) {
@@ -350,22 +357,22 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, ep->epf_bar[bar] =NULL ; } -static unsigned int dw_pcie_ep_get_rebar_offset (struct dw_pcie *pci, +static unsigned int dw_pcie_ep_get_rebar_offset (struct dw_pcie_ep *ep,u8 func_no,enum pci_barno bar) {u32 reg, bar_index;unsigned int offset, nbars;int i; - offset =dw_pcie_find_ext_capability (pci,PCI_EXT_CAP_ID_REBAR ); + offset =dw_pcie_ep_find_ext_capability (ep, func_no,PCI_EXT_CAP_ID_REBAR );if (!offset)return offset; - reg =dw_pcie_readl_dbi (pci, offset +PCI_REBAR_CTRL ); + reg =dw_pcie_ep_readl_dbi (ep, func_no, offset +PCI_REBAR_CTRL ); nbars =FIELD_GET (PCI_REBAR_CTRL_NBAR_MASK , reg);for (i =0 ; i < nbars; i++, offset +=PCI_REBAR_CTRL ) { - reg =dw_pcie_readl_dbi (pci, offset +PCI_REBAR_CTRL ); + reg =dw_pcie_ep_readl_dbi (ep, func_no, offset +PCI_REBAR_CTRL ); bar_index =FIELD_GET (PCI_REBAR_CTRL_BAR_IDX , reg);if (bar_index == bar)return offset;
@@ -386,7 +393,7 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no,u32 rebar_cap, rebar_ctrl;int ret; - rebar_offset =dw_pcie_ep_get_rebar_offset (pci, bar); + rebar_offset =dw_pcie_ep_get_rebar_offset (ep, func_no, bar);if (!rebar_offset)return -EINVAL ;
@@ -416,16 +423,16 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no,* 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes" * bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB. */ - rebar_ctrl =dw_pcie_readl_dbi (pci, rebar_offset +PCI_REBAR_CTRL ); + rebar_ctrl =dw_pcie_ep_readl_dbi (ep, func_no, rebar_offset +PCI_REBAR_CTRL ); rebar_ctrl &= ~GENMASK (31 ,16 ); -dw_pcie_writel_dbi (pci, rebar_offset +PCI_REBAR_CTRL , rebar_ctrl); +dw_pcie_ep_writel_dbi (ep, func_no, rebar_offset +PCI_REBAR_CTRL , rebar_ctrl);/* * The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically * updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR * Example for 32-bit Memory BAR0" in DWC EP databook 5.96a. */ -dw_pcie_writel_dbi (pci, rebar_offset +PCI_REBAR_CAP , rebar_cap); +dw_pcie_ep_writel_dbi (ep, func_no, rebar_offset +PCI_REBAR_CAP , rebar_cap);dw_pcie_dbi_ro_wr_dis (pci);
@@ -1023,20 +1030,17 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) }EXPORT_SYMBOL_GPL (dw_pcie_ep_deinit); -static void dw_pcie_ep_init_non_sticky_registers (struct dw_pcie *pci) +static void dw_pcie_ep_init_rebar_registers (struct dw_pcie_ep *ep,u8 func_no) { -struct dw_pcie_ep *ep = &pci->ep;unsigned int offset;unsigned int nbars;enum pci_barno bar;u32 reg, i, val; - offset =dw_pcie_find_ext_capability (pci,PCI_EXT_CAP_ID_REBAR ); - -dw_pcie_dbi_ro_wr_en (pci); + offset =dw_pcie_ep_find_ext_capability (ep, func_no,PCI_EXT_CAP_ID_REBAR );if (offset) { - reg =dw_pcie_readl_dbi (pci, offset +PCI_REBAR_CTRL ); + reg =dw_pcie_ep_readl_dbi (ep, func_no, offset +PCI_REBAR_CTRL ); nbars =FIELD_GET (PCI_REBAR_CTRL_NBAR_MASK , reg);/*
@@ -1057,16 +1061,28 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)* the controller when RESBAR_CAP_REG is written, which * is why RESBAR_CAP_REG is written here. */ - val =dw_pcie_readl_dbi (pci, offset +PCI_REBAR_CTRL ); + val =dw_pcie_ep_readl_dbi (ep, func_no, offset +PCI_REBAR_CTRL ); bar =FIELD_GET (PCI_REBAR_CTRL_BAR_IDX , val);if (ep->epf_bar[bar])pci_epc_bar_size_to_rebar_cap (ep->epf_bar[bar]->size, &val);else val =BIT (4 ); -dw_pcie_writel_dbi (pci, offset +PCI_REBAR_CAP , val); +dw_pcie_ep_writel_dbi (ep, func_no, offset +PCI_REBAR_CAP , val); } } +} + +static void dw_pcie_ep_init_non_sticky_registers (struct dw_pcie *pci) +{ +struct dw_pcie_ep *ep = &pci->ep; +u8 funcs = ep->epc->max_functions; +u8 func_no; + +dw_pcie_dbi_ro_wr_en (pci); + +for (func_no =0 ; func_no < funcs; func_no++) +dw_pcie_ep_init_rebar_registers (ep, func_no);dw_pcie_setup (pci);dw_pcie_dbi_ro_wr_dis (pci);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 399/781 PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aksh Garg <[email protected]>
[ Upstream commit 72cb5ed2a5c6d87f71a409347f7d3b228fee6bee ]
The commit 24ede430fa49 ("PCI: designware-ep: Add multiple PFs support for DWC") added support for multiple PFs in the DWC driver, but the implementation was incomplete. It did not properly support MSI/MSI-X, as well as BAR and inbound ATU mapping for multiple PFs. The MSI/MSI-X issue was later fixed by commit 47a062609a30 ("PCI: designware-ep:
Modify MSI and MSIX CAP way of finding") by introducing a per-PF struct dw_pcie_ep_func.
However, even with both commits, the multiple PF support in the driver remains broken because BAR configuration and ATU mappings are managed globally in struct dw_pcie_ep, meaning all PFs share the same BAR-to-ATU mapping table. This causes one PF's EPF to overwrite the address translation of another PF's EPF in the internal ATU region, creating conflicts when multiple physical functions attempt to configure their BARs independently.
The commit cfbc98dbf44d ("PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU") later introduced Address Match Mode support, which suffers from the same multi-PF conflict issue.
Fix this by moving the required members from struct dw_pcie_ep to struct dw_pcie_ep_func, similar to what commit 47a062609a30 ("PCI: designware-ep: Modify MSI and MSIX CAP way of finding") did for MSI/MSI-X capability support, to allow proper multi-function endpoint operation, where each PF can configure its BARs and corresponding internal ATU region without interfering with other PFs.
--- .../pci/controller/dwc/pcie-designware-ep.c | 79 +++++++++++-------- drivers/pci/controller/dwc/pcie-designware.h | 12 +-- 2 files changed, 54 insertions(+), 37 deletions(-)
drivers/pci/controller/dwc/pcie-designware-ep.c +48 -31
@@ -115,11 +115,15 @@ static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,int ret;u32 free_win;struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no); -if (!ep->bar_to_atu[bar]) +if (!ep_func) +return -EINVAL ; + +if (!ep_func->bar_to_atu[bar]) free_win =find_first_zero_bit (ep->ib_window_map, pci->num_ib_windows);else - free_win = ep->bar_to_atu[bar] -1 ; + free_win = ep_func->bar_to_atu[bar] -1 ;if (free_win >= pci->num_ib_windows) {dev_err (pci->dev,"No free inbound window\n" );
@@ -137,33 +141,37 @@ static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,* Always increment free_win before assignment, since value 0 is used to identify * unallocated mapping. */ - ep->bar_to_atu[bar] = free_win +1 ; + ep_func->bar_to_atu[bar] = free_win +1 ;set_bit (free_win, ep->ib_window_map);return 0 ; } -static void dw_pcie_ep_clear_ib_maps (struct dw_pcie_ep *ep,enum pci_barno bar) +static void dw_pcie_ep_clear_ib_maps (struct dw_pcie_ep *ep,u8 func_no,enum pci_barno bar) { +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no);struct dw_pcie *pci =to_dw_pcie_from_ep (ep);struct device *dev = pci->dev;unsigned int i, num;u32 atu_index;u32 *indexes; +if (!ep_func) +return ; +/* Tear down the BAR Match Mode mapping, if any. */ -if (ep->bar_to_atu[bar]) { - atu_index = ep->bar_to_atu[bar] -1 ; +if (ep_func->bar_to_atu[bar]) { + atu_index = ep_func->bar_to_atu[bar] -1 ;dw_pcie_disable_atu (pci,PCIE_ATU_REGION_DIR_IB , atu_index);clear_bit (atu_index, ep->ib_window_map); - ep->bar_to_atu[bar] =0 ; + ep_func->bar_to_atu[bar] =0 ; }/* Tear down all Address Match Mode mappings, if any. */ - indexes = ep->ib_atu_indexes[bar]; - num = ep->num_ib_atu_indexes[bar]; - ep->ib_atu_indexes[bar] =NULL ; - ep->num_ib_atu_indexes[bar] =0 ; + indexes = ep_func->ib_atu_indexes[bar]; + num = ep_func->num_ib_atu_indexes[bar]; + ep_func->ib_atu_indexes[bar] =NULL ; + ep_func->num_ib_atu_indexes[bar] =0 ;if (!indexes)return ;for (i =0 ; i < num; i++) {
@@ -248,6 +256,7 @@ static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep,static int dw_pcie_ep_ib_atu_addr (struct dw_pcie_ep *ep,u8 func_no,int type,const struct pci_epf_bar *epf_bar) { +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no);const struct pci_epf_bar_submap *submap = epf_bar->submap;struct dw_pcie *pci =to_dw_pcie_from_ep (ep);enum pci_barno bar = epf_bar->barno;
@@ -258,7 +267,7 @@ static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,unsigned int i;u32 *indexes; -if (!epf_bar->num_submap || !submap || !epf_bar->size) +if (!ep_func || !epf_bar->num_submap || !submap || !epf_bar->size)return -EINVAL ; ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap,
@@ -279,8 +288,8 @@ static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,if (!indexes)return -ENOMEM ; - ep->ib_atu_indexes[bar] = indexes; - ep->num_ib_atu_indexes[bar] =0 ; + ep_func->ib_atu_indexes[bar] = indexes; + ep_func->num_ib_atu_indexes[bar] =0 ;for (i =0 ; i < epf_bar->num_submap; i++) { size = submap[i].size;
@@ -308,11 +317,11 @@ static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,set_bit (free_win, ep->ib_window_map); indexes[i] = free_win; - ep->num_ib_atu_indexes[bar] = i +1 ; + ep_func->num_ib_atu_indexes[bar] = i +1 ; }return 0 ; err: -dw_pcie_ep_clear_ib_maps (ep, bar); +dw_pcie_ep_clear_ib_maps (ep, func_no, bar);return ret; }
@@ -346,15 +355,16 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,struct dw_pcie_ep *ep =epc_get_drvdata (epc);struct dw_pcie *pci =to_dw_pcie_from_ep (ep);enum pci_barno bar = epf_bar->barno; +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no); -if (!ep->epf_bar[bar]) +if (!ep_func || !ep_func->epf_bar[bar])return ;__dw_pcie_ep_reset_bar (pci, func_no, bar, epf_bar->flags); -dw_pcie_ep_clear_ib_maps (ep, bar); +dw_pcie_ep_clear_ib_maps (ep, func_no, bar); - ep->epf_bar[bar] =NULL ; + ep_func->epf_bar[bar] =NULL ; } static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie_ep *ep, u8 func_no,
@@ -481,12 +491,16 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, {struct dw_pcie_ep *ep =epc_get_drvdata (epc);struct dw_pcie *pci =to_dw_pcie_from_ep (ep); +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no);enum pci_barno bar = epf_bar->barno;size_t size = epf_bar->size;enum pci_epc_bar_type bar_type;int flags = epf_bar->flags;int ret, type; +if (!ep_func) +return -EINVAL ; +/* * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs * 1 and 2 to form a 64-bit BAR.
@@ -500,22 +514,22 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,* calling clear_bar() would clear the BAR's PCI address assigned by the * host). */ -if (ep->epf_bar[bar]) { +if (ep_func->epf_bar[bar]) {/* * We can only dynamically change a BAR if the new BAR size and * BAR flags do not differ from the existing configuration. */ -if (ep->epf_bar[bar]->barno != bar || - ep->epf_bar[bar]->size != size || - ep->epf_bar[bar]->flags != flags) +if (ep_func->epf_bar[bar]->barno != bar || + ep_func->epf_bar[bar]->size != size || + ep_func->epf_bar[bar]->flags != flags)return -EINVAL ;/* * When dynamically changing a BAR, tear down any existing * mappings before re-programming. */ -if (ep->epf_bar[bar]->num_submap || epf_bar->num_submap) -dw_pcie_ep_clear_ib_maps (ep, bar); +if (ep_func->epf_bar[bar]->num_submap || epf_bar->num_submap) +dw_pcie_ep_clear_ib_maps (ep, func_no, bar);/* * When dynamically changing a BAR, skip writing the BAR reg, as
@@ -574,7 +588,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,if (ret)return ret; - ep->epf_bar[bar] = epf_bar; + ep_func->epf_bar[bar] = epf_bar;return 0 ; }
@@ -969,7 +983,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, bir =FIELD_GET (PCI_MSIX_TABLE_BIR , tbl_offset); tbl_offset &=PCI_MSIX_TABLE_OFFSET ; - msix_tbl = ep->epf_bar[bir]->addr + tbl_offset; + msix_tbl = ep_func->epf_bar[bir]->addr + tbl_offset; msg_addr = msix_tbl[(interrupt_num -1 )].msg_addr; msg_data = msix_tbl[(interrupt_num -1 )].msg_data; vec_ctrl = msix_tbl[(interrupt_num -1 )].vector_ctrl;
@@ -1032,11 +1046,14 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit);static void dw_pcie_ep_init_rebar_registers (struct dw_pcie_ep *ep,u8 func_no) { -unsigned int offset; -unsigned int nbars; +struct dw_pcie_ep_func *ep_func =dw_pcie_ep_get_func_from_ep (ep, func_no); +unsigned int offset, nbars;enum pci_barno bar;u32 reg, i, val; +if (!ep_func) +return ; + offset =dw_pcie_ep_find_ext_capability (ep, func_no,PCI_EXT_CAP_ID_REBAR );if (offset) {
@@ -1063,8 +1080,8 @@ static void dw_pcie_ep_init_rebar_registers(struct dw_pcie_ep *ep, u8 func_no)*/ val =dw_pcie_ep_readl_dbi (ep, func_no, offset +PCI_REBAR_CTRL ); bar =FIELD_GET (PCI_REBAR_CTRL_BAR_IDX , val); -if (ep->epf_bar[bar]) -pci_epc_bar_size_to_rebar_cap (ep->epf_bar[bar]->size, &val); +if (ep_func->epf_bar[bar]) +pci_epc_bar_size_to_rebar_cap (ep_func->epf_bar[bar]->size, &val);else val =BIT (4 );
drivers/pci/controller/dwc/pcie-designware.h +6 -6
@@ -467,6 +467,12 @@ struct dw_pcie_ep_func {u8 func_no;u8 msi_cap;/* MSI capability offset */ u8 msix_cap;/* MSI-X capability offset */ +u8 bar_to_atu[PCI_STD_NUM_BARS ]; +struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS ]; + +/* Only for Address Match Mode inbound iATU */ +u32 *ib_atu_indexes[PCI_STD_NUM_BARS ]; +unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS ]; };struct dw_pcie_ep {
@@ -476,17 +482,11 @@ struct dw_pcie_ep {phys_addr_t phys_base;size_t addr_size;size_t page_size; -u8 bar_to_atu[PCI_STD_NUM_BARS ];phys_addr_t *outbound_addr;unsigned long *ib_window_map;unsigned long *ob_window_map;void __iomem *msi_mem;phys_addr_t msi_mem_phys; -struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS ]; - -/* Only for Address Match Mode inbound iATU */ -u32 *ib_atu_indexes[PCI_STD_NUM_BARS ]; -unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS ];/* MSI outbound iATU state */ bool msi_iatu_mapped;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 400/781 of: unittest: fix possible null-pointer dereferences in of_unittest_property_copy()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tuo Li <[email protected]>
[ Upstream commit d289cb7fcefe41a54d8f9c6d0e0947f5f82b15c6 ]
This function first duplicates p1 and p2 into new, and then checks whether the duplication succeeds. However, if the duplication fails (e.g., kzalloc() returns NULL in __of_prop_dup()), new will be NULL but is still dereferenced in __of_prop_free(). To ensure that the unit test continues to run even when duplication fails, add a NULL check before calling __of_prop_free().
--- drivers/of/unittest.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/of/unittest.c +4 -2
@@ -804,11 +804,13 @@ static void __init of_unittest_property_copy(void) new =__of_prop_dup (&p1,GFP_KERNEL );unittest (new &&propcmp (&p1, new),"empty property didn't copy correctly\n" ); -__of_prop_free (new); +if (new) +__of_prop_free (new); new =__of_prop_dup (&p2,GFP_KERNEL );unittest (new &&propcmp (&p2, new),"non-empty property didn't copy correctly\n" ); -__of_prop_free (new); +if (new) +__of_prop_free (new);#endif }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 401/781 mptcp: do not account for OoO in mptcp_rcvbuf_grow()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 6b329393502e5857662b851a13f947209c588587 ]
MPTCP-level OoOs are physiological when multiple subflows are active concurrently and will not cause retransmissions nor are caused by drops.
Accounting for them in mptcp_rcvbuf_grow() causes the rcvbuf slowly drifting towards tcp_rmem[2].
Remove such accounting. Note that subflows will still account for TCP-level OoO when the MPTCP-level rcvbuf is propagated.
This also closes a subtle and very unlikely race condition with rcvspace init; active sockets with user-space holding the msk-level socket lock, could complete such initialization in the receive callback, after that the first OoO data reaches the rcvbuf and potentially triggering a divide by zero Oops.
--- net/mptcp/protocol.c | 6 ------ 1 file changed, 6 deletions(-)
net/mptcp/protocol.c +0 -6
@@ -224,9 +224,6 @@ static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)do_div (grow, oldval); rcvwin += grow <<1 ; -if (!RB_EMPTY_ROOT (&msk->out_of_order_queue)) - rcvwin +=MPTCP_SKB_CB (msk->ooo_last_skb)->end_seq - msk->ack_seq; - cap =READ_ONCE (net->ipv4.sysctl_tcp_rmem[2 ]); rcvbuf =min_t (u32,mptcp_space_from_win (sk, rcvwin), cap);
@@ -350,9 +347,6 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb) end:skb_condense (skb);skb_set_owner_r (skb, sk); -/* do not grow rcvbuf for not-yet-accepted or orphaned sockets. */ -if (sk->sk_socket) -mptcp_rcvbuf_grow (sk, msk->rcvq_space.space); } static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 402/781 mptcp: fix receive space timestamp initialization
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 70274765fef555af92a1532d5bd5450c691fca9d ]
MPTCP initialize the receive buffer stamp in mptcp_rcv_space_init(), using the provided subflow stamp. Such helper is invoked in several places; for passive sockets, space init happened at clone time.
In such scenario, MPTCP ends-up accesses the subflow stamp before its initialization, leading to quite randomic timing for the first receive buffer auto-tune event, as the timestamp for newly created subflow is not refreshed there.
Fix the issue moving the stamp initialization out of the mentioned helper, at the data transfer start, and always using a fresh timestamp.
--- net/mptcp/protocol.c | 8 ++++---- net/mptcp/protocol.h | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-)
net/mptcp/protocol.c +4 -4
@@ -2082,8 +2082,8 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied) msk->rcvq_space.copied += copied; - mstamp =div_u64 (tcp_clock_ns (),NSEC_PER_USEC ); - time =tcp_stamp_us_delta (mstamp, msk->rcvq_space.time); + mstamp =mptcp_stamp (); + time =tcp_stamp_us_delta (mstamp,READ_ONCE (msk->rcvq_space.time)); rtt_us = msk->rcvq_space.rtt_us;if (rtt_us && time < (rtt_us >>3 ))
@@ -3543,6 +3543,7 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,__mptcp_propagate_sndbuf (nsk, ssk);mptcp_rcv_space_init (msk, ssk); + msk->rcvq_space.time =mptcp_stamp ();if (mp_opt->suboptions &OPTION_MPTCP_MPC_ACK )__mptcp_subflow_fully_established (msk, subflow, mp_opt);
@@ -3560,8 +3561,6 @@ void mptcp_rcv_space_init(struct mptcp_sock *msk, const struct sock *ssk) msk->rcvq_space.copied =0 ; msk->rcvq_space.rtt_us =0 ; - msk->rcvq_space.time = tp->tcp_mstamp; -/* initial rcv_space offering made to peer */ msk->rcvq_space.space =min_t (u32, tp->rcv_wnd,TCP_INIT_CWND * tp->advmss);
@@ -3757,6 +3756,7 @@ void mptcp_finish_connect(struct sock *ssk)* accessing the field below */ WRITE_ONCE (msk->local_key, subflow->local_key); +WRITE_ONCE (msk->rcvq_space.time,mptcp_stamp ());mptcp_pm_new_connection (msk, ssk,0 ); }
net/mptcp/protocol.h +5 -0
@@ -915,6 +915,11 @@ static inline bool mptcp_is_fully_established(struct sock *sk)READ_ONCE (mptcp_sk (sk)->fully_established); } +static inline u64 mptcp_stamp (void ) +{ +return div_u64 (tcp_clock_ns (),NSEC_PER_USEC ); +} +void mptcp_rcv_space_init (struct mptcp_sock *msk,const struct sock *ssk);void mptcp_data_ready (struct sock *sk,struct sock *ssk);bool mptcp_finish_join (struct sock *sk);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 403/781 octeontx2-af: Fix PF driver crash with kexec kernel booting
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anshumali Gaur <[email protected]>
[ Upstream commit 2d2d574309e3ae84ee794869a5da8b4c38753a94 ]
During a kexec reboot the hardware is not power-cycled, so AF state from the old kernel can persist into the new kernel. When AF and PF drivers are built as modules, the PF driver may probe before AF reinitializes the hardware.
The PF driver treats the RVUM block revision as an indication that AF initialization is complete. If this value is left uncleared at shutdown, PF may incorrectly assume AF is ready and access stale hardware state, leading to a crash.
Clear the RVUM block revision during AF shutdown to avoid PF mis-detecting AF readiness after kexec.
--- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +11 -0
@@ -3632,11 +3632,22 @@ static void rvu_remove(struct pci_dev *pdev)devm_kfree (&pdev->dev, rvu); } +static void rvu_shutdown (struct pci_dev *pdev) +{ +struct rvu *rvu =pci_get_drvdata (pdev); + +if (!rvu) +return ; + +rvu_clear_rvum_blk_revid (rvu); +} +static struct pci_driver rvu_driver = { .name =DRV_NAME , .id_table = rvu_id_table, .probe = rvu_probe, .remove = rvu_remove, + .shutdown = rvu_shutdown, };static int __init rvu_init_module (void )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 404/781 bonding: only set speed/duplex to unknown, if getting speed failed
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Bogendoerfer <[email protected]>
[ Upstream commit 48dec8d88af96039a4a17b8c2f148f2a4066e195 ]
bond_update_speed_duplex() first set speed/duplex to unknown and then asks slave driver for current speed/duplex. Since getting speed/duplex might take longer there is a race, where this false state is visible by /proc/net/bonding. With commit 691b2bf14946 ("bonding:
update port speed when getting bond speed") this race gets more visible,
if user space is calling ethtool on a regular base.
Fix this by only setting speed/duplex to unknown, if link speed is really unknown/unusable.
--- drivers/net/bonding/bond_main.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/net/bonding/bond_main.c +9 -6
@@ -791,26 +791,29 @@ static int bond_update_speed_duplex(struct slave *slave)struct ethtool_link_ksettings ecmd;int res; - slave->speed =SPEED_UNKNOWN ; - slave->duplex =DUPLEX_UNKNOWN ; - res =__ethtool_get_link_ksettings (slave_dev, &ecmd);if (res <0 ) -return 1 ; + goto speed_duplex_unknown;if (ecmd.base.speed ==0 || ecmd.base.speed == ((__u32)-1 )) -return 1 ; + goto speed_duplex_unknown;switch (ecmd.base.duplex) {case DUPLEX_FULL :case DUPLEX_HALF :break ;default : -return 1 ; + goto speed_duplex_unknown; } slave->speed = ecmd.base.speed; slave->duplex = ecmd.base.duplex;return 0 ; + +speed_duplex_unknown: + slave->speed =SPEED_UNKNOWN ; + slave->duplex =DUPLEX_UNKNOWN ; + +return 1 ; }const char *bond_slave_link_status (s8 link)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 405/781 inet: RAW sockets using IPPROTO_RAW MUST drop incoming ICMP
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c89477ad79446867394360b29bb801010fc3ff22 ]
Yizhou Zhao reported that simply having one RAW socket on protocol IPPROTO_RAW (255) was dangerous.
socket(AF_INET, SOCK_RAW, 255);
A malicious incoming ICMP packet can set the protocol field to 255 and match this socket, leading to FNHE cache changes.
inner = IP(src="192.168.2.1", dst="8.8.8.8", proto=255)/Raw("TEST")
pkt = IP(src="192.168.1.1", dst="192.168.2.1")/ICMP(type=3, code=4, nexthopmtu=576)/inner
"man 7 raw" states:
A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is able to send any IP protocol that is specified in the passed header. Receiving of all IP protocols via IPPROTO_RAW is not possible using raw sockets.
Make sure we drop these malicious packets.
--- net/ipv4/icmp.c | 14 ++++++++++---- net/ipv6/icmp.c | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-)
net/ipv4/icmp.c +10 -4
@@ -1031,16 +1031,22 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)/* Checkin full IP header plus 8 bytes of protocol to * avoid additional coding at protocol handlers. */ -if (!pskb_may_pull (skb, iph->ihl *4 +8 )) { -__ICMP_INC_STATS (dev_net_rcu (skb->dev),ICMP_MIB_INERRORS ); -return ; - } +if (!pskb_may_pull (skb, iph->ihl *4 +8 )) + goto out; + +/* IPPROTO_RAW sockets are not supposed to receive anything. */ +if (protocol ==IPPROTO_RAW ) + goto out;raw_icmp_error (skb, protocol, info); ipprot =rcu_dereference (inet_protos[protocol]);if (ipprot && ipprot->err_handler) ipprot->err_handler (skb, info); +return ; + +out: +__ICMP_INC_STATS (dev_net_rcu (skb->dev),ICMP_MIB_INERRORS ); }static bool icmp_tag_validation (int proto)
net/ipv6/icmp.c +6 -0
@@ -1066,6 +1066,12 @@ enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,if (reason !=SKB_NOT_DROPPED_YET ) goto out; +if (nexthdr ==IPPROTO_RAW ) { +/* Add a more specific reason later ? */ + reason =SKB_DROP_REASON_NOT_SPECIFIED ; + goto out; + } +/* BUGGG_FUTURE: we should try to parse exthdrs in this packet. Without this we will not able f.e. to make source routed pmtu discovery.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 406/781 nfc: hci: shdlc: Stop timers and work before freeing context
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Votokina Victoria <[email protected]>
[ Upstream commit c9efde1e537baed7648a94022b43836a348a074f ]
llc_shdlc_deinit() purges SHDLC skb queues and frees the llc_shdlc structure while its timers and state machine work may still be active.
Timer callbacks can schedule sm_work, and sm_work accesses SHDLC state and the skb queues. If teardown happens in parallel with a queued/running work item, it can lead to UAF and other shutdown races.
Stop all SHDLC timers and cancel sm_work synchronously before purging the queues and freeing the context.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- net/nfc/hci/llc_shdlc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/nfc/hci/llc_shdlc.c +8 -0
@@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) {struct llc_shdlc *shdlc =nfc_llc_get_data (llc); +timer_shutdown_sync (&shdlc->connect_timer); +timer_shutdown_sync (&shdlc->t1_timer); +timer_shutdown_sync (&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + +cancel_work_sync (&shdlc->sm_work); +skb_queue_purge (&shdlc->rcv_q);skb_queue_purge (&shdlc->send_q);skb_queue_purge (&shdlc->ack_pending_q);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 407/781 amd-xgbe: do not select NET_SELFTESTS when INET is disabled
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raju Rangoju <[email protected]>
[ Upstream commit ee9241524b4682a34ed4b66d8c68c33304810b93 ]
AMD_XGBE currently selects NET_SELFTESTS unconditionally. Since select does not honor dependencies, this can force-enable NET_SELFTESTS even when INET is disabled (e.g. INET=n randconfig builds).
Fixes build issue when INET is disabled.
--- drivers/net/ethernet/amd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/amd/Kconfig +1 -1
@@ -165,7 +165,7 @@ config AMD_XGBE select CRC32 select PHYLIB select AMD_XGBE_HAVE_ECC if X86 - select NET_SELFTESTS + imply NET_SELFTESTS help This driver supports the AMD 10GbE Ethernet device found on an AMD SoC.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 408/781 netfilter: nfnetlink_queue: optimize verdict lookup with hash table
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Scott Mitchell <[email protected]>
[ Upstream commit e19079adcd26a25d7d3e586b1837493361fdf8b6 ]
The current implementation uses a linear list to find queued packets by ID when processing verdicts from userspace. With large queue depths and out-of-order verdicting, this O(n) lookup becomes a significant bottleneck, causing userspace verdict processing to dominate CPU time.
Replace the linear search with a hash table for O(1) average-case packet lookup by ID. A global rhashtable spanning all network namespaces attributes hash bucket memory to kernel but is subject to fixed upper bound.
Stable-dep-of: 207b3ebacb61 ("netfilter: nfnetlink_queue: do shared-unconfirmed check before segmentation")
--- include/net/netfilter/nf_queue.h | 3 + net/netfilter/nfnetlink_queue.c | 146 ++++++++++++++++++++++++------- 2 files changed, 119 insertions(+), 30 deletions(-)
include/net/netfilter/nf_queue.h +3 -0
@@ -6,11 +6,13 @@#include <linux/ipv6.h> #include <linux/jhash.h> #include <linux/netfilter.h> +#include <linux/rhashtable-types.h> #include <linux/skbuff.h> /* Each queued (to userspace) skbuff has one of these. */ struct nf_queue_entry {struct list_head list; +struct rhash_head hash_node;struct sk_buff *skb;unsigned int id;unsigned int hook_index;/* index in hook_entries->hook[] */
@@ -20,6 +22,7 @@ struct nf_queue_entry {#endif struct nf_hook_state state;u16 size;/* sizeof(entry) + saved route keys */ +u16 queue_num;/* extra space to store route keys */ };
net/netfilter/nfnetlink_queue.c +116 -30
245 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 409/781 netfilter: nfnetlink_queue: do shared-unconfirmed check before segmentation
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 207b3ebacb6113acaaec0d171d5307032c690004 ]
Ulrich reports a regression with nfqueue:
If an application did not set the 'F_GSO' capability flag and a gso packet with an unconfirmed nf_conn entry is received all packets are now dropped instead of queued, because the check happens after skb_gso_segment(). In that case, we did have exclusive ownership of the skb and its associated conntrack entry. The elevated use count is due to skb_clone happening via skb_gso_segment().
Move the check so that its peformed vs. the aggregated packet.
Then, annotate the individual segments except the first one so we can do a 2nd check at reinject time.
For the normal case, where userspace does in-order reinjects, this avoids packet drops: first reinjected segment continues traversal and confirms entry, remaining segments observe the confirmed entry.
While at it, simplify nf_ct_drop_unconfirmed(): We only care about unconfirmed entries with a refcnt > 1, there is no need to special-case dying entries.
This only happens with UDP. With TCP, the only unconfirmed packet will be the TCP SYN, those aren't aggregated by GRO.
Next patch adds a udpgro test case to cover this scenario.
--- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nfnetlink_queue.c | 123 +++++++++++++++++++------------ 2 files changed, 75 insertions(+), 49 deletions(-)
include/net/netfilter/nf_queue.h +1 -0
@@ -21,6 +21,7 @@ struct nf_queue_entry {struct net_device *physout;#endif struct nf_hook_state state; +bool nf_ct_is_unconfirmed;u16 size;/* sizeof(entry) + saved route keys */ u16 queue_num;
net/netfilter/nfnetlink_queue.c +74 -49
173 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 410/781 netfilter: nft_set_hash: fix get operation on big endian
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 2f635adbe2642d398a0be3ab245accd2987be0c3 ]
tests/shell/testcases/packetpath/set_match_nomatch_hash_fast fails on big endian with:
Error: Could not process rule: No such file or directory reset element ip test s { 244.147.90.126 } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Fatal: Cannot fetch element "244.147.90.126"
... because the wrong bucket is searched, jhash() and jhash1_word are not interchangeable on big endian.
--- net/netfilter/nft_set_hash.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/netfilter/nft_set_hash.c +7 -2
@@ -619,15 +619,20 @@ static struct nft_elem_priv * nft_hash_get(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, unsigned int flags) { + const u32 *key = (const u32 *)&elem->key.val;struct nft_hash *priv =nft_set_priv (set);u8 genmask =nft_genmask_cur (net);struct nft_hash_elem *he;u32 hash; - hash =jhash (elem->key.val.data, set->klen, priv->seed); +if (set->klen ==4 ) + hash =jhash_1word (*key, priv->seed); +else + hash =jhash (key, set->klen, priv->seed); + hash =reciprocal_scale (hash, priv->buckets);hlist_for_each_entry_rcu (he, &priv->table[hash], node) { -if (!memcmp (nft_set_ext_key (&he->ext), elem->key.val.data, set->klen) && +if (!memcmp (nft_set_ext_key (&he->ext), key, set->klen) &&nft_set_elem_active (&he->ext, genmask))return &he->priv; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 411/781 netfilter: nft_counter: fix reset of counters on 32bit archs
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anders Grahn <[email protected]>
[ Upstream commit 1e13f27e0675552161ab1778be9a23a636dde8a7 ]
nft_counter_reset() calls u64_stats_add() with a negative value to reset the counter. This will work on 64bit archs, hence the negative value added will wrap as a 64bit value which then can wrap the stat counter as well.
On 32bit archs, the added negative value will wrap as a 32bit value and _not_ wrapping the stat counter properly. In most cases, this would just lead to a very large 32bit value being added to the stat counter.
Fix by introducing u64_stats_sub().
--- include/linux/u64_stats_sync.h | 10 ++++++++++ net/netfilter/nft_counter.c | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-)
include/linux/u64_stats_sync.h +10 -0
@@ -89,6 +89,11 @@ static inline void u64_stats_add(u64_stats_t *p, unsigned long val)local64_add (val, &p->v); } +static inline void u64_stats_sub (u64_stats_t *p,s64 val) +{ +local64_sub (val, &p->v); +} +static inline void u64_stats_inc (u64_stats_t *p) {local64_inc (&p->v);
@@ -130,6 +135,11 @@ static inline void u64_stats_add(u64_stats_t *p, unsigned long val) p->v += val; } +static inline void u64_stats_sub (u64_stats_t *p,s64 val) +{ + p->v -= val; +} +static inline void u64_stats_inc (u64_stats_t *p) { p->v++;
net/netfilter/nft_counter.c +2 -2
@@ -117,8 +117,8 @@ static void nft_counter_reset(struct nft_counter_percpu_priv *priv, nft_sync =this_cpu_ptr (&nft_counter_sync);u64_stats_update_begin (nft_sync); -u64_stats_add (&this_cpu->packets, -total->packets); -u64_stats_add (&this_cpu->bytes, -total->bytes); +u64_stats_sub (&this_cpu->packets, total->packets); +u64_stats_sub (&this_cpu->bytes, total->bytes);u64_stats_update_end (nft_sync);local_bh_enable ();
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 412/781 netfilter: nft_set_rbtree: fix bogus EEXIST with NLM_F_CREATE with null interval
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 7f9203f41aae8eea74fba6a3370da41332eabcda ]
Userspace adds a non-matching null element to the kernel for historical reasons. This null element is added when the set is populated with elements. Inclusion of this element is conditional, therefore, userspace needs to dump the set content to check for its presence.
If the NLM_F_CREATE flag is turned on, this becomes an issue because kernel bogusly reports EEXIST.
Add special case to ignore NLM_F_CREATE in this case, therefore, re-adding the nul-element never fails.
--- net/netfilter/nf_tables_api.c | 5 +++++ net/netfilter/nft_set_rbtree.c | 13 +++++++++++++ 2 files changed, 18 insertions(+)
net/netfilter/nf_tables_api.c +5 -0
@@ -7635,6 +7635,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,* and an existing one. */ err = -EEXIST ; + }else if (err == -ECANCELED ) { +/* ECANCELED reports an existing nul-element in +* interval sets. +*/ + err =0 ; } goto err_element_clash; }
net/netfilter/nft_set_rbtree.c +13 -0
@@ -39,6 +39,13 @@ static bool nft_rbtree_interval_start(const struct nft_rbtree_elem *rbe)return !nft_rbtree_interval_end (rbe); } +static bool nft_rbtree_interval_null (const struct nft_set *set, +const struct nft_rbtree_elem *rbe) +{ +return (!memchr_inv (nft_set_ext_key (&rbe->ext),0 , set->klen) && +nft_rbtree_interval_end (rbe)); +} +static int nft_rbtree_cmp (const struct nft_set *set,const struct nft_rbtree_elem *e1,const struct nft_rbtree_elem *e2)
@@ -431,6 +438,12 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,*/ if (rbe_le && !nft_rbtree_cmp (set, new, rbe_le) &&nft_rbtree_interval_end (rbe_le) ==nft_rbtree_interval_end (new)) { +/* - ignore null interval, otherwise NLM_F_CREATE bogusly +* reports EEXIST. +*/ +if (nft_rbtree_interval_null (set, new)) +return -ECANCELED ; + *elem_priv = &rbe_le->priv;return -EEXIST ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 413/781 netfilter: nft_set_rbtree: check for partial overlaps in anonymous sets
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 4780ec142cbb24b794129d3080eee5cac2943ffc ]
Userspace provides an optimized representation in case intervals are adjacent, where the end element is omitted.
The existing partial overlap detection logic skips anonymous set checks on start elements for this reason.
However, it is possible to add intervals that overlap to this anonymous where two start elements with the same, eg. A-B, A-C where C < B.
start end
A B
start end
A C
Restore the check on overlapping start elements to report an overlap.
--- net/netfilter/nft_set_rbtree.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-)
net/netfilter/nft_set_rbtree.c +25 -5
@@ -309,11 +309,23 @@ static bool nft_rbtree_update_first(const struct nft_set *set,return false; } +/* Only for anonymous sets which do not allow updates, all element are active. */ +static struct nft_rbtree_elem *nft_rbtree_prev_active (struct nft_rbtree_elem *rbe) +{ +struct rb_node *node; + + node =rb_prev (&rbe->node); +if (!node) +return NULL ; + + return rb_entry(node, struct nft_rbtree_elem, node); +} +static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new,struct nft_elem_priv **elem_priv) { -struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL ; +struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);
@@ -451,11 +463,19 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,/* - new start element with existing closest, less or equal key value * being a start element: partial overlap, reported as -ENOTEMPTY. * Anonymous sets allow for two consecutive start element since they -* are constant, skip them to avoid bogus overlap reports. +* are constant, but validate that this new start element does not +* sit in between an existing start and end elements: partial overlap, +* reported as -ENOTEMPTY. */ -if (!nft_set_is_anonymous (set) && rbe_le && -nft_rbtree_interval_start (rbe_le) &&nft_rbtree_interval_start (new)) -return -ENOTEMPTY ; +if (rbe_le && +nft_rbtree_interval_start (rbe_le) &&nft_rbtree_interval_start (new)) { +if (!nft_set_is_anonymous (set)) +return -ENOTEMPTY ; + + rbe_prev =nft_rbtree_prev_active (rbe_le); +if (rbe_prev &&nft_rbtree_interval_end (rbe_prev)) +return -ENOTEMPTY ; + }/* - new end element with existing closest, less or equal key value * being a end element: partial overlap, reported as -ENOTEMPTY.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 414/781 netfilter: nft_set_rbtree: translate rbtree to array for binary search
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 7e43e0a1141deec651a60109dab3690854107298 ]
The rbtree can temporarily store overlapping inactive elements during the transaction processing, leading to false negative lookups.
To address this issue, this patch adds a .commit function that walks the the rbtree to build a array of intervals of ordered elements. This conversion compacts the two singleton elements that represent the start and the end of the interval into a single interval object for space efficient.
Binary search is O(log n), similar to rbtree lookup time, therefore, performance number should be similar, and there is an implementation available under lib/bsearch.c and include/linux/bsearch.h that is used for this purpose.
This slightly increases memory consumption for this new array that stores pointers to the start and the end of the interval.
With this patch:
# time nft -f 100k-intervals-set.nft
real 0m4.218s user 0m3.544s sys 0m0.400s
Without this patch:
# time nft -f 100k-intervals-set.nft
real 0m3.920s user 0m3.547s sys 0m0.276s
With this patch, with IPv4 intervals:
baseline rbtree (match on first field only): 15254954pps
Without this patch:
baseline rbtree (match on first field only): 10256119pps
This provides a ~50% improvement in matching intervals from packet path.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 341 +++++++++++++++++++++++++-------- 1 file changed, 257 insertions(+), 84 deletions(-)
net/netfilter/nft_set_rbtree.c +257 -84
@@ -10,14 +10,29 @@#include <linux/module.h> #include <linux/list.h> #include <linux/rbtree.h> +#include <linux/bsearch.h> #include <linux/netlink.h> #include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables_core.h> +struct nft_array_interval { +struct nft_set_ext *from; +struct nft_set_ext *to; +}; + +struct nft_array { +u32 max_intervals; +u32 num_intervals; +struct nft_array_interval *intervals; +struct rcu_head rcu_head; +}; +struct nft_rbtree {struct rb_root root;rwlock_t lock; +struct nft_array __rcu *array; +struct nft_array *array_next;seqcount_rwlock_t count;unsigned long last_gc; };
@@ -54,90 +69,6 @@ static int nft_rbtree_cmp(const struct nft_set *set, set->klen); } -static bool nft_rbtree_elem_expired (const struct nft_rbtree_elem *rbe) -{ -return nft_set_elem_expired (&rbe->ext); -} - -static const struct nft_set_ext * -__nft_rbtree_lookup (const struct net *net,const struct nft_set *set, -const u32 *key,unsigned int seq) -{ -struct nft_rbtree *priv =nft_set_priv (set); -const struct nft_rbtree_elem *rbe, *interval =NULL ; -u8 genmask =nft_genmask_cur (net); -const struct rb_node *parent; -int d; - - parent =rcu_dereference_raw (priv->root.rb_node); -while (parent !=NULL ) { -if (read_seqcount_retry (&priv->count, seq)) -return NULL ; - - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - d =memcmp (nft_set_ext_key (&rbe->ext), key, set->klen); -if (d <0 ) { - parent =rcu_dereference_raw (parent->rb_left); -if (interval && - !nft_rbtree_cmp (set, rbe, interval) && -nft_rbtree_interval_end (rbe) && -nft_rbtree_interval_start (interval)) -continue ; -if (nft_set_elem_active (&rbe->ext, genmask) && - !nft_rbtree_elem_expired (rbe)) - interval = rbe; - }else if (d >0 ) - parent =rcu_dereference_raw (parent->rb_right); -else { -if (!nft_set_elem_active (&rbe->ext, genmask)) { - parent =rcu_dereference_raw (parent->rb_left); -continue ; - } - -if (nft_rbtree_elem_expired (rbe)) -return NULL ; - -if (nft_rbtree_interval_end (rbe)) { -if (nft_set_is_anonymous (set)) -return NULL ; - parent =rcu_dereference_raw (parent->rb_left); - interval =NULL ; -continue ; - } - -return &rbe->ext; - } - } - -if (set->flags &NFT_SET_INTERVAL && interval !=NULL && -nft_rbtree_interval_start (interval)) -return &interval->ext; - -return NULL ; -} - -INDIRECT_CALLABLE_SCOPE -const struct nft_set_ext * -nft_rbtree_lookup (const struct net *net,const struct nft_set *set, -const u32 *key) -{ -struct nft_rbtree *priv =nft_set_priv (set); -unsigned int seq =read_seqcount_begin (&priv->count); -const struct nft_set_ext *ext; - - ext =__nft_rbtree_lookup (net, set, key, seq); -if (ext || !read_seqcount_retry (&priv->count, seq)) -return ext; - -read_lock_bh (&priv->lock); - seq =read_seqcount_begin (&priv->count); - ext =__nft_rbtree_lookup (net, set, key, seq); -read_unlock_bh (&priv->lock); - -return ext; -} -static bool __nft_rbtree_get (const struct net *net,const struct nft_set *set,const u32 *key,struct nft_rbtree_elem **elem,unsigned int seq,unsigned int flags,u8 genmask)
@@ -228,6 +159,60 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,return &rbe->priv; } +struct nft_array_lookup_ctx { +const u32 *key; +u32 klen; +}; + +static int nft_array_lookup_cmp (const void *pkey,const void *entry) +{ +const struct nft_array_interval *interval = entry; +const struct nft_array_lookup_ctx *ctx = pkey; +int a, b; + +if (!interval->from) +return 1 ; + + a =memcmp (ctx->key,nft_set_ext_key (interval->from), ctx->klen); +if (!interval->to) + b =-1 ; +else + b =memcmp (ctx->key,nft_set_ext_key (interval->to), ctx->klen); + +if (a >=0 && b <0 ) +return 0 ; + +if (a <0 ) +return -1 ; + +return 1 ; +} + +INDIRECT_CALLABLE_SCOPE +const struct nft_set_ext * +nft_rbtree_lookup (const struct net *net,const struct nft_set *set, +const u32 *key) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array =rcu_dereference (priv->array); +const struct nft_array_interval *interval; +struct nft_array_lookup_ctx ctx = { + .key = key, + .klen = set->klen, + }; + +if (!array) +return NULL ; + + interval =bsearch (&ctx, array->intervals, array->num_intervals, +sizeof (struct nft_array_interval ), + nft_array_lookup_cmp); +if (!interval ||nft_set_elem_expired (interval->from)) +return NULL ; + +return interval->from; +} +static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set,struct nft_rbtree *priv,struct nft_rbtree_elem *rbe)
@@ -514,6 +499,87 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,return 0 ; } +static int nft_array_intervals_alloc (struct nft_array *array,u32 max_intervals) +{ +struct nft_array_interval *intervals; + + intervals =kvcalloc (max_intervals,sizeof (struct nft_array_interval ), +GFP_KERNEL_ACCOUNT ); +if (!intervals) +return -ENOMEM ; + +if (array->intervals) +kvfree (array->intervals); + + array->intervals = intervals; + array->max_intervals = max_intervals; + +return 0 ; +} + +static struct nft_array *nft_array_alloc (u32 max_intervals) +{ +struct nft_array *array; + + array =kzalloc (sizeof (*array),GFP_KERNEL_ACCOUNT ); +if (!array) +return NULL ; + +if (nft_array_intervals_alloc (array, max_intervals) <0 ) { +kfree (array); +return NULL ; + } + +return array; +} + +#define NFT_ARRAY_EXTRA_SIZE 10240 + +/* Similar to nft_rbtree_{u,k}size to hide details to userspace, but consider +* packed representation coming from userspace for anonymous sets too. +*/ +static u32 nft_array_elems (const struct nft_set *set) +{ +u32 nelems =atomic_read (&set->nelems); + +/* Adjacent intervals are represented with a single start element in +* anonymous sets, use the current element counter as is. +*/ +if (nft_set_is_anonymous (set)) +return nelems; + +/* Add extra room for never matching interval at the beginning and open +* interval at the end which only use a single element to represent it. +* The conversion to array will compact intervals, this allows reduce +* memory consumption. +*/ +return (nelems /2 ) +2 ; +} + +static int nft_array_may_resize (const struct nft_set *set) +{ +u32 nelems =nft_array_elems (set), new_max_intervals; +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array; + +if (!priv->array_next) { + array =nft_array_alloc (nelems +NFT_ARRAY_EXTRA_SIZE ); +if (!array) +return -ENOMEM ; + + priv->array_next = array; + } + +if (nelems < priv->array_next->max_intervals) +return 0 ; + + new_max_intervals = priv->array_next->max_intervals +NFT_ARRAY_EXTRA_SIZE ; +if (nft_array_intervals_alloc (priv->array_next, new_max_intervals) <0 ) +return -ENOMEM ; + +return 0 ; +} +static int nft_rbtree_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem,struct nft_elem_priv **elem_priv)
@@ -522,6 +588,9 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,struct nft_rbtree *priv =nft_set_priv (set);int err; +if (nft_array_may_resize (set) <0 ) +return -ENOMEM ; + do { if (fatal_signal_pending(current)) return -EINTR;
@@ -586,6 +655,9 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,u64 tstamp =nft_net_tstamp (net);int d; +if (nft_array_may_resize (set) <0 ) +return NULL ; +while (parent !=NULL ) { rbe = rb_entry(parent, struct nft_rbtree_elem, node);
@@ -648,6 +720,11 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,switch (iter->type) {case NFT_ITER_UPDATE :lockdep_assert_held (&nft_pernet (ctx->net)->commit_mutex); + +if (nft_array_may_resize (set) <0 ) { + iter->err = -ENOMEM ; +break ; + }nft_rbtree_do_walk (ctx, set, iter);break ;case NFT_ITER_READ :
@@ -750,14 +827,24 @@ static int nft_rbtree_init(const struct nft_set *set,seqcount_rwlock_init (&priv->count, &priv->lock); priv->root =RB_ROOT ; + priv->array =NULL ; + priv->array_next =NULL ; +return 0 ; } +static void __nft_array_free (struct nft_array *array) +{ +kvfree (array->intervals); +kfree (array); +} +static void nft_rbtree_destroy (const struct nft_ctx *ctx,const struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set);struct nft_rbtree_elem *rbe; +struct nft_array *array;struct rb_node *node;while ((node = priv->root.rb_node) !=NULL ) {
@@ -765,6 +852,12 @@ static void nft_rbtree_destroy(const struct nft_ctx *ctx, rbe = rb_entry(node, struct nft_rbtree_elem, node);nf_tables_set_elem_destroy (ctx, set, &rbe->priv); } + + array =rcu_dereference_protected (priv->array, true); +if (array) +__nft_array_free (array); +if (priv->array_next) +__nft_array_free (priv->array_next); } static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
@@ -785,12 +878,91 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,return true; } +static void nft_array_free_rcu (struct rcu_head *rcu_head) +{ + struct nft_array *array = container_of(rcu_head, struct nft_array, rcu_head); + +__nft_array_free (array); +} +static void nft_rbtree_commit (struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree_elem *rbe, *prev_rbe; +struct nft_array *old; +u32 num_intervals =0 ; +struct rb_node *node;if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set)))nft_rbtree_gc (set); + +/* No changes, skip, eg. elements updates only. */ +if (!priv->array_next) +return ; + +/* Reverse walk to create an array from smaller to largest interval. */ + node =rb_last (&priv->root); +if (node) + prev_rbe = rb_entry(node, struct nft_rbtree_elem, node); +else + prev_rbe =NULL ; + +while (prev_rbe) { + rbe = prev_rbe; + +if (nft_rbtree_interval_start (rbe)) + priv->array_next->intervals[num_intervals].from = &rbe->ext; +else if (nft_rbtree_interval_end (rbe)) + priv->array_next->intervals[num_intervals++].to = &rbe->ext; + +if (num_intervals >= priv->array_next->max_intervals) { +pr_warn_once ("malformed interval set from userspace?" ); + goto err_out; + } + + node =rb_prev (node); +if (!node) +break ; + + prev_rbe = rb_entry(node, struct nft_rbtree_elem, node); + +/* For anonymous sets, when adjacent ranges are found, +* the end element is not added to the set to pack the set +* representation. Use next start element to complete this +* interval. +*/ +if (nft_rbtree_interval_start (rbe) && +nft_rbtree_interval_start (prev_rbe) && + priv->array_next->intervals[num_intervals].from) + priv->array_next->intervals[num_intervals++].to = &prev_rbe->ext; + +if (num_intervals >= priv->array_next->max_intervals) { +pr_warn_once ("malformed interval set from userspace?" ); + goto err_out; + } + } + +if (priv->array_next->intervals[num_intervals].from) + num_intervals++; +err_out: + priv->array_next->num_intervals = num_intervals; + old =rcu_replace_pointer (priv->array, priv->array_next, true); + priv->array_next =NULL ; +if (old) +call_rcu (&old->rcu_head, nft_array_free_rcu); +} + +static void nft_rbtree_abort (const struct nft_set *set) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array_next; + +if (!priv->array_next) +return ; + + array_next = priv->array_next; + priv->array_next =NULL ; +__nft_array_free (array_next); }static void nft_rbtree_gc_init (const struct nft_set *set)
@@ -854,6 +1026,7 @@ const struct nft_set_type nft_set_rbtree_type = { .flush = nft_rbtree_flush, .activate = nft_rbtree_activate, .commit = nft_rbtree_commit, + .abort = nft_rbtree_abort, .gc_init = nft_rbtree_gc_init, .lookup = nft_rbtree_lookup, .walk = nft_rbtree_walk,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 415/781 netfilter: nft_set_rbtree: use binary search array in get command
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 2aa34191f06fc5af4f70241518a8554370d86054 ]
Rework .get interface to use the binary search array, this needs a specific lookup function to match on end intervals (<=). Packet path lookup is slight different because match is on lesser value, not equal (ie. <).
After this patch, seqcount can be removed in a follow up patch.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 154 ++++++++++++++------------------- 1 file changed, 64 insertions(+), 90 deletions(-)
net/netfilter/nft_set_rbtree.c +64 -90
@@ -69,96 +69,6 @@ static int nft_rbtree_cmp(const struct nft_set *set, set->klen); } -static bool __nft_rbtree_get (const struct net *net,const struct nft_set *set, -const u32 *key,struct nft_rbtree_elem **elem, -unsigned int seq,unsigned int flags,u8 genmask) -{ -struct nft_rbtree_elem *rbe, *interval =NULL ; -struct nft_rbtree *priv =nft_set_priv (set); -const struct rb_node *parent; -const void *this; -int d; - - parent =rcu_dereference_raw (priv->root.rb_node); -while (parent !=NULL ) { -if (read_seqcount_retry (&priv->count, seq)) -return false; - - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - this =nft_set_ext_key (&rbe->ext); - d =memcmp (this, key, set->klen); -if (d <0 ) { - parent =rcu_dereference_raw (parent->rb_left); -if (!(flags &NFT_SET_ELEM_INTERVAL_END )) - interval = rbe; - }else if (d >0 ) { - parent =rcu_dereference_raw (parent->rb_right); -if (flags &NFT_SET_ELEM_INTERVAL_END ) - interval = rbe; - }else { -if (!nft_set_elem_active (&rbe->ext, genmask)) { - parent =rcu_dereference_raw (parent->rb_left); -continue ; - } - -if (nft_set_elem_expired (&rbe->ext)) -return false; - -if (!nft_set_ext_exists (&rbe->ext,NFT_SET_EXT_FLAGS ) || - (*nft_set_ext_flags (&rbe->ext) &NFT_SET_ELEM_INTERVAL_END ) == - (flags &NFT_SET_ELEM_INTERVAL_END )) { - *elem = rbe; -return true; - } - -if (nft_rbtree_interval_end (rbe)) - interval =NULL ; - - parent =rcu_dereference_raw (parent->rb_left); - } - } - -if (set->flags &NFT_SET_INTERVAL && interval !=NULL && -nft_set_elem_active (&interval->ext, genmask) && - !nft_set_elem_expired (&interval->ext) && - ((!nft_rbtree_interval_end (interval) && - !(flags &NFT_SET_ELEM_INTERVAL_END )) || - (nft_rbtree_interval_end (interval) && - (flags &NFT_SET_ELEM_INTERVAL_END )))) { - *elem = interval; -return true; - } - -return false; -} - -static struct nft_elem_priv * -nft_rbtree_get (const struct net *net,const struct nft_set *set, -const struct nft_set_elem *elem,unsigned int flags) -{ -struct nft_rbtree *priv =nft_set_priv (set); -unsigned int seq =read_seqcount_begin (&priv->count); -struct nft_rbtree_elem *rbe =ERR_PTR (-ENOENT ); -const u32 *key = (const u32 *)&elem->key.val; -u8 genmask =nft_genmask_cur (net); -bool ret; - - ret =__nft_rbtree_get (net, set, key, &rbe, seq, flags, genmask); -if (ret || !read_seqcount_retry (&priv->count, seq)) -return &rbe->priv; - -read_lock_bh (&priv->lock); - seq =read_seqcount_begin (&priv->count); - ret =__nft_rbtree_get (net, set, key, &rbe, seq, flags, genmask); -read_unlock_bh (&priv->lock); - -if (!ret) -return ERR_PTR (-ENOENT ); - -return &rbe->priv; -} -struct nft_array_lookup_ctx {const u32 *key;u32 klen;
@@ -213,6 +123,70 @@ nft_rbtree_lookup(const struct net *net, const struct nft_set *set,return interval->from; } +struct nft_array_get_ctx { +const u32 *key; +unsigned int flags; +u32 klen; +}; + +static int nft_array_get_cmp (const void *pkey,const void *entry) +{ +const struct nft_array_interval *interval = entry; +const struct nft_array_get_ctx *ctx = pkey; +int a, b; + +if (!interval->from) +return 1 ; + + a =memcmp (ctx->key,nft_set_ext_key (interval->from), ctx->klen); +if (!interval->to) + b =-1 ; +else + b =memcmp (ctx->key,nft_set_ext_key (interval->to), ctx->klen); + +if (a >=0 ) { +if (ctx->flags &NFT_SET_ELEM_INTERVAL_END && b <=0 ) +return 0 ; +else if (b <0 ) +return 0 ; + } + +if (a <0 ) +return -1 ; + +return 1 ; +} + +static struct nft_elem_priv * +nft_rbtree_get (const struct net *net,const struct nft_set *set, +const struct nft_set_elem *elem,unsigned int flags) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array =rcu_dereference (priv->array); +const struct nft_array_interval *interval; +struct nft_array_get_ctx ctx = { + .key = (const u32 *)&elem->key.val, + .flags = flags, + .klen = set->klen, + }; +struct nft_rbtree_elem *rbe; + +if (!array) +return ERR_PTR (-ENOENT ); + + interval =bsearch (&ctx, array->intervals, array->num_intervals, +sizeof (struct nft_array_interval ), nft_array_get_cmp); +if (!interval ||nft_set_elem_expired (interval->from)) +return ERR_PTR (-ENOENT ); + +if (flags &NFT_SET_ELEM_INTERVAL_END ) + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); +else + rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + +return &rbe->priv; +} +static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set,struct nft_rbtree *priv,struct nft_rbtree_elem *rbe)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 416/781 netfilter: nft_set_rbtree: remove seqcount_rwlock_t
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 5599fa810b503eafc2bd8cd15bd45f35fc8ff6b9 ]
After the conversion to binary search array, this is not required anymore. Remove it.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 6 ------ 1 file changed, 6 deletions(-)
net/netfilter/nft_set_rbtree.c +0 -6
@@ -33,7 +33,6 @@ struct nft_rbtree {rwlock_t lock;struct nft_array __rcu *array;struct nft_array *array_next; -seqcount_rwlock_t count;unsigned long last_gc; };
@@ -572,9 +571,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); -write_seqcount_begin (&priv->count); err =__nft_rbtree_insert (net, set, rbe, elem_priv); -write_seqcount_end (&priv->count);write_unlock_bh (&priv->lock); }while (err == -EAGAIN );
@@ -584,9 +581,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,static void nft_rbtree_erase (struct nft_rbtree *priv,struct nft_rbtree_elem *rbe) {write_lock_bh (&priv->lock); -write_seqcount_begin (&priv->count);rb_erase (&rbe->node, &priv->root); -write_seqcount_end (&priv->count);write_unlock_bh (&priv->lock); }
@@ -798,7 +793,6 @@ static int nft_rbtree_init(const struct nft_set *set,BUILD_BUG_ON (offsetof(struct nft_rbtree_elem , priv) !=0 );rwlock_init (&priv->lock); -seqcount_rwlock_init (&priv->count, &priv->lock); priv->root =RB_ROOT ; priv->array =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 417/781 netfilter: nft_set_rbtree: dont gc elements on insert
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 35f83a75529a829b0939708b003652f7b4f3df9a ]
During insertion we can queue up expired elements for garbage collection.
In case of later abort, the commit hook will never be called. Packet path and 'get' requests will find free'd elements in the binary search blob:
nft_set_ext_key include/net/netfilter/nf_tables.h:800 [inline] nft_array_get_cmp+0x1f6/0x2a0 net/netfilter/nft_set_rbtree.c:133 __inline_bsearch include/linux/bsearch.h:15 [inline] bsearch+0x50/0xc0 lib/bsearch.c:33 nft_rbtree_get+0x16b/0x400 net/netfilter/nft_set_rbtree.c:169 nft_setelem_get net/netfilter/nf_tables_api.c:6495 [inline] nft_get_set_elem+0x420/0xaa0 net/netfilter/nf_tables_api.c:6543 nf_tables_getsetelem+0x448/0x5e0 net/netfilter/nf_tables_api.c:6632 nfnetlink_rcv_msg+0x8ae/0x12c0 net/netfilter/nfnetlink.c:290
Also, when we insert an element that triggers -EEXIST, and that insertion happens to also zap a timed-out entry, we end up with same issue:
Neither commit nor abort hook is called.
Fix this by removing gc api usage during insertion.
The blamed commit also removes concurrency of the rbtree with the packet path, so we can now safely rb_erase() the element and move it to a new expired list that can be reaped in the commit hook before building the next blob iteration.
This also avoids the need to rebuild the blob in the abort path:
Expired elements seen during insertion attempts are kept around until a transaction passes.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 136 ++++++++++++++++----------------- 1 file changed, 68 insertions(+), 68 deletions(-)
net/netfilter/nft_set_rbtree.c +68 -68
@@ -34,11 +34,15 @@ struct nft_rbtree {struct nft_array __rcu *array;struct nft_array *array_next;unsigned long last_gc; +struct list_head expired; };struct nft_rbtree_elem {struct nft_elem_priv priv; -struct rb_node node; +union { +struct rb_node node; +struct list_head list; + };struct nft_set_ext ext; };
@@ -186,13 +190,16 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,return &rbe->priv; } -static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set, -struct nft_rbtree *priv, -struct nft_rbtree_elem *rbe) +static void nft_rbtree_gc_elem_move (struct net *net,struct nft_set *set, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) {lockdep_assert_held_write (&priv->lock);nft_setelem_data_deactivate (net, set, &rbe->priv);rb_erase (&rbe->node, &priv->root); + +/* collected later on in commit callback */ +list_add (&rbe->list, &priv->expired); } static const struct nft_rbtree_elem *
@@ -203,11 +210,6 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv,struct rb_node *prev =rb_prev (&rbe->node);struct net *net =read_pnet (&set->net);struct nft_rbtree_elem *rbe_prev; -struct nft_trans_gc *gc; - - gc =nft_trans_gc_alloc (set,0 ,GFP_ATOMIC ); -if (!gc) -return ERR_PTR (-ENOMEM );/* search for end interval coming before this element. * end intervals don't carry a timeout extension, they
@@ -225,28 +227,10 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv, rbe_prev =NULL ;if (prev) { rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node); -nft_rbtree_gc_elem_remove (net, set, priv, rbe_prev); - -/* There is always room in this trans gc for this element, -* memory allocation never actually happens, hence, the warning -* splat in such case. No need to set NFT_SET_ELEM_DEAD_BIT, -* this is synchronous gc which never fails. -*/ - gc =nft_trans_gc_queue_sync (gc,GFP_ATOMIC ); -if (WARN_ON_ONCE (!gc)) -return ERR_PTR (-ENOMEM ); - -nft_trans_gc_elem_add (gc, rbe_prev); +nft_rbtree_gc_elem_move (net, set, priv, rbe_prev); } -nft_rbtree_gc_elem_remove (net, set, priv, rbe); - gc =nft_trans_gc_queue_sync (gc,GFP_ATOMIC ); -if (WARN_ON_ONCE (!gc)) -return ERR_PTR (-ENOMEM ); - -nft_trans_gc_elem_add (gc, rbe); - -nft_trans_gc_queue_sync_done (gc); +nft_rbtree_gc_elem_move (net, set, priv, rbe);return rbe_prev; }
@@ -708,29 +692,13 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, } } -static void nft_rbtree_gc_remove (struct net *net,struct nft_set *set, -struct nft_rbtree *priv, -struct nft_rbtree_elem *rbe) -{ -nft_setelem_data_deactivate (net, set, &rbe->priv); -nft_rbtree_erase (priv, rbe); -} - -static void nft_rbtree_gc (struct nft_set *set) +static void nft_rbtree_gc_scan (struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set);struct nft_rbtree_elem *rbe, *rbe_end =NULL ;struct net *net =read_pnet (&set->net);u64 tstamp =nft_net_tstamp (net);struct rb_node *node, *next; -struct nft_trans_gc *gc; - - set =nft_set_container_of (priv); - net =read_pnet (&set->net); - - gc =nft_trans_gc_alloc (set,0 ,GFP_KERNEL ); -if (!gc) -return ;for (node =rb_first (&priv->root); node ; node = next) { next =rb_next (node);
@@ -748,34 +716,46 @@ static void nft_rbtree_gc(struct nft_set *set)if (!__nft_set_elem_expired (&rbe->ext, tstamp))continue ; - gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); -if (!gc) - goto try_later; -/* end element needs to be removed first, it has * no timeout extension. */ +write_lock_bh (&priv->lock);if (rbe_end) { -nft_rbtree_gc_remove (net, set, priv, rbe_end); -nft_trans_gc_elem_add (gc, rbe_end); +nft_rbtree_gc_elem_move (net, set, priv, rbe_end); rbe_end =NULL ; } - gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); -if (!gc) - goto try_later; - -nft_rbtree_gc_remove (net, set, priv, rbe); -nft_trans_gc_elem_add (gc, rbe); +nft_rbtree_gc_elem_move (net, set, priv, rbe); +write_unlock_bh (&priv->lock); } -try_later: + priv->last_gc = jiffies; +} + +static void nft_rbtree_gc_queue (struct nft_set *set) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree_elem *rbe, *rbe_end; +struct nft_trans_gc *gc; + +if (list_empty (&priv->expired)) +return ; -if (gc) { - gc =nft_trans_gc_catchall_sync (gc); -nft_trans_gc_queue_sync_done (gc); - priv->last_gc = jiffies; + gc =nft_trans_gc_alloc (set,0 ,GFP_KERNEL ); +if (!gc) +return ; + +list_for_each_entry_safe (rbe, rbe_end, &priv->expired, list) { +list_del (&rbe->list); +nft_trans_gc_elem_add (gc, rbe); + + gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); +if (!gc) +return ; } + + gc =nft_trans_gc_catchall_sync (gc); +nft_trans_gc_queue_sync_done (gc); } static u64 nft_rbtree_privsize(const struct nlattr * const nla[],
@@ -794,6 +774,7 @@ static int nft_rbtree_init(const struct nft_set *set,rwlock_init (&priv->lock); priv->root =RB_ROOT ; +INIT_LIST_HEAD (&priv->expired); priv->array =NULL ; priv->array_next =NULL ;
@@ -811,10 +792,15 @@ static void nft_rbtree_destroy(const struct nft_ctx *ctx, const struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set); -struct nft_rbtree_elem *rbe; +struct nft_rbtree_elem *rbe, *next;struct nft_array *array;struct rb_node *node; +list_for_each_entry_safe (rbe, next, &priv->expired, list) { +list_del (&rbe->list); +nf_tables_set_elem_destroy (ctx, set, &rbe->priv); + } +while ((node = priv->root.rb_node) !=NULL ) {rb_erase (node, &priv->root); rbe = rb_entry(node, struct nft_rbtree_elem, node);
@@ -861,13 +847,21 @@ static void nft_rbtree_commit(struct nft_set *set)u32 num_intervals =0 ;struct rb_node *node; -if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set))) -nft_rbtree_gc (set); -/* No changes, skip, eg. elements updates only. */ if (!priv->array_next)return ; +/* GC can be performed if the binary search blob is going +* to be rebuilt. It has to be done in two phases: first +* scan tree and move all expired elements to the expired +* list. +* +* Then, after blob has been re-built and published to other +* CPUs, queue collected entries for freeing. +*/ +if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set))) +nft_rbtree_gc_scan (set); +/* Reverse walk to create an array from smaller to largest interval. */ node =rb_last (&priv->root);if (node)
@@ -914,10 +908,16 @@ static void nft_rbtree_commit(struct nft_set *set) num_intervals++; err_out: priv->array_next->num_intervals = num_intervals; - old =rcu_replace_pointer (priv->array, priv->array_next, true); + old =rcu_replace_pointer (priv->array, priv->array_next, +lockdep_is_held (&nft_pernet (read_pnet (&set->net))->commit_mutex)); priv->array_next =NULL ;if (old)call_rcu (&old->rcu_head, nft_array_free_rcu); + +/* New blob is public, queue collected entries for freeing. +* call_rcu ensures elements stay around until readers are done. +*/ +nft_rbtree_gc_queue (set); }static void nft_rbtree_abort (const struct nft_set *set)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 418/781 netfilter: nft_set_rbtree: validate element belonging to interval
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 782f2688128eca6d05a48be1c247f68d86afc168 ]
The existing partial overlap detection does not check if the elements belong to the interval, eg.
add element inet x y { 1.1.1.1-2.2.2.2, 4.4.4.4-5.5.5.5 } add element inet x y { 1.1.1.1-5.5.5.5 } => this should fail: ENOENT
Similar situation occurs with deletions:
add element inet x y { 1.1.1.1-2.2.2.2, 4.4.4.4-5.5.5.5} delete element inet x y { 1.1.1.1-5.5.5.5 } => this should fail: ENOENT
This currently works via mitigation by nft in userspace, which is performing the overlap detection before sending the elements to the kernel. This requires a previous netlink dump of the set content which slows down incremental updates on interval sets, because a netlink set content dump is needed.
This patch extends the existing overlap detection to track the most recent start element that already exists. The pointer to the existing start element is stored as a cookie (no pointer dereference is ever possible). If the end element is added and it already exists, then check that the existing end element is adjacent to the already existing start element. Similar logic applies to element deactivation.
This patch also annotates the timestamp to identify if start cookie comes from an older batch, in such case reset it. Otherwise, a failing create element command leaves the start cookie in place, resulting in bogus error reporting.
There is still a few more corner cases of overlap detection related to the open interval that are addressed in follow up patches.
This is address an early design mistake where an interval is expressed as two elements, using the NFT_SET_ELEM_INTERVAL_END flag, instead of the more recent NFTA_SET_ELEM_KEY_END attribute that pipapo already uses.
--- net/netfilter/nft_set_rbtree.c | 147 ++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 4 deletions(-)
net/netfilter/nft_set_rbtree.c +143 -4
@@ -33,8 +33,10 @@ struct nft_rbtree {rwlock_t lock;struct nft_array __rcu *array;struct nft_array *array_next; +unsigned long start_rbe_cookie;unsigned long last_gc;struct list_head expired; +u64 last_tstamp; };struct nft_rbtree_elem {
@@ -263,16 +265,85 @@ static struct nft_rbtree_elem *nft_rbtree_prev_active(struct nft_rbtree_elem *rb return rb_entry(node, struct nft_rbtree_elem, node); } +static struct nft_rbtree_elem * +__nft_rbtree_next_active (struct rb_node *node,u8 genmask) +{ +struct nft_rbtree_elem *next_rbe; + +while (node) { + next_rbe = rb_entry(node, struct nft_rbtree_elem, node); +if (!nft_set_elem_active (&next_rbe->ext, genmask)) { + node =rb_next (node); +continue ; + } + +return next_rbe; + } + +return NULL ; +} + +static struct nft_rbtree_elem * +nft_rbtree_next_active (struct nft_rbtree_elem *rbe,u8 genmask) +{ +return __nft_rbtree_next_active (rb_next (&rbe->node), genmask); +} + +static void nft_rbtree_maybe_reset_start_cookie (struct nft_rbtree *priv, +u64 tstamp) +{ +if (priv->last_tstamp != tstamp) { + priv->start_rbe_cookie =0 ; + priv->last_tstamp = tstamp; + } +} + +static void nft_rbtree_set_start_cookie (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe) +{ + priv->start_rbe_cookie = (unsigned long )rbe; +} + +static bool nft_rbtree_cmp_start_cookie (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe) +{ +return priv->start_rbe_cookie == (unsigned long )rbe; +} + +static bool nft_rbtree_insert_same_interval (const struct net *net, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) +{ +u8 genmask =nft_genmask_next (net); +struct nft_rbtree_elem *next_rbe; + +if (!priv->start_rbe_cookie) +return true; + + next_rbe =nft_rbtree_next_active (rbe, genmask); +if (next_rbe) { +/* Closest start element differs from last element added. */ +if (nft_rbtree_interval_start (next_rbe) && +nft_rbtree_cmp_start_cookie (priv, next_rbe)) { + priv->start_rbe_cookie =0 ; +return true; + } + } + + priv->start_rbe_cookie =0 ; + +return false; +} +static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new, -struct nft_elem_priv **elem_priv) +struct nft_elem_priv **elem_priv,u64 tstamp) {struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);u8 genmask =nft_genmask_next (net); -u64 tstamp =nft_net_tstamp (net);int d;/* Descend the tree to search for an existing element greater than the
@@ -378,12 +449,18 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, } } +if (nft_rbtree_interval_null (set, new)) + priv->start_rbe_cookie =0 ; +else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) + priv->start_rbe_cookie =0 ; +/* - new start element matching existing start element: full overlap * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given. */ if (rbe_ge && !nft_rbtree_cmp (set, new, rbe_ge) &&nft_rbtree_interval_start (rbe_ge) ==nft_rbtree_interval_start (new)) { *elem_priv = &rbe_ge->priv; +nft_rbtree_set_start_cookie (priv, rbe_ge);return -EEXIST ; }
@@ -399,6 +476,11 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,return -ECANCELED ; *elem_priv = &rbe_le->priv; + +/* - start and end element belong to the same interval. */ +if (!nft_rbtree_insert_same_interval (net, priv, rbe_le)) +return -ENOTEMPTY ; +return -EEXIST ; }
@@ -543,8 +625,11 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, {struct nft_rbtree_elem *rbe =nft_elem_priv_cast (elem->priv);struct nft_rbtree *priv =nft_set_priv (set); +u64 tstamp =nft_net_tstamp (net);int err; +nft_rbtree_maybe_reset_start_cookie (priv, tstamp); +if (nft_array_may_resize (set) <0 )return -ENOMEM ;
@@ -555,7 +640,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); - err =__nft_rbtree_insert (net, set, rbe, elem_priv); + err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp);write_unlock_bh (&priv->lock); }while (err == -EAGAIN );
@@ -588,6 +673,48 @@ static void nft_rbtree_activate(const struct net *net,nft_clear (net, &rbe->ext); } +static struct nft_rbtree_elem * +nft_rbtree_next_inactive (struct nft_rbtree_elem *rbe,u8 genmask) +{ +struct nft_rbtree_elem *next_rbe; +struct rb_node *node; + + node =rb_next (&rbe->node); +if (node) { + next_rbe = rb_entry(node, struct nft_rbtree_elem, node); +if (nft_rbtree_interval_start (next_rbe) && + !nft_set_elem_active (&next_rbe->ext, genmask)) +return next_rbe; + } + +return NULL ; +} + +static bool nft_rbtree_deactivate_same_interval (const struct net *net, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) +{ +u8 genmask =nft_genmask_next (net); +struct nft_rbtree_elem *next_rbe; + +if (!priv->start_rbe_cookie) +return true; + + next_rbe =nft_rbtree_next_inactive (rbe, genmask); +if (next_rbe) { +/* Closest start element differs from last element added. */ +if (nft_rbtree_interval_start (next_rbe) && +nft_rbtree_cmp_start_cookie (priv, next_rbe)) { + priv->start_rbe_cookie =0 ; +return true; + } + } + + priv->start_rbe_cookie =0 ; + +return false; +} +static void nft_rbtree_flush (const struct net *net,const struct nft_set *set,struct nft_elem_priv *elem_priv)
@@ -602,12 +729,18 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) {struct nft_rbtree_elem *rbe, *this =nft_elem_priv_cast (elem->priv); -const struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree *priv =nft_set_priv (set);const struct rb_node *parent = priv->root.rb_node;u8 genmask =nft_genmask_next (net);u64 tstamp =nft_net_tstamp (net);int d; +nft_rbtree_maybe_reset_start_cookie (priv, tstamp); + +if (nft_rbtree_interval_start (this) || +nft_rbtree_interval_null (set, this)) + priv->start_rbe_cookie =0 ; +if (nft_array_may_resize (set) <0 )return NULL ;
@@ -635,6 +768,12 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, parent = parent->rb_left;continue ; } + +if (nft_rbtree_interval_start (rbe)) +nft_rbtree_set_start_cookie (priv, rbe); +else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe)) +return NULL ; +nft_rbtree_flush (net, set, &rbe->priv);return &rbe->priv; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 419/781 netfilter: nft_set_rbtree: validate open interval overlap
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 648946966a08e4cb1a71619e3d1b12bd7642de7b ]
Open intervals do not have an end element, in particular an open interval at the end of the set is hard to validate because of it is lacking the end element, and interval validation relies on such end element to perform the checks.
This patch adds a new flag field to struct nft_set_elem, this is not an issue because this is a temporary object that is allocated in the stack from the insert/deactivate path. This flag field is used to specify that this is the last element in this add/delete command.
The last flag is used, in combination with the start element cookie, to check if there is a partial overlap, eg.
Already exists: 255.255.255.0-255.255.255.254
Add interval: 255.255.255.0-255.255.255.255
~~~~~~~~~~~~~
start element overlap
Basically, the idea is to check for an existing end element in the set if there is an overlap with an existing start element.
However, the last open interval can come in any position in the add command, the corner case can get a bit more complicated:
Already exists: 255.255.255.0-255.255.255.254
Add intervals: 255.255.255.0-255.255.255.255,255.255.255.0-255.255.255.254
~~~~~~~~~~~~~
start element overlap
To catch this overlap, annotate that the new start element is a possible overlap, then report the overlap if the next element is another start element that confirms that previous element in an open interval at the end of the set.
For deletions, do not update the start cookie when deleting an open interval, otherwise this can trigger spurious EEXIST when adding new elements.
Unfortunately, there is no NFT_SET_ELEM_INTERVAL_OPEN flag which would make easier to detect open interval overlaps.
--- include/net/netfilter/nf_tables.h | 4 ++ net/netfilter/nf_tables_api.c | 21 +++++++-- net/netfilter/nft_set_rbtree.c | 71 ++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-)
include/net/netfilter/nf_tables.h +4 -0
@@ -278,6 +278,8 @@ struct nft_userdata {unsigned char data[]; }; +#define NFT_SET_ELEM_INTERNAL_LAST 0x1 +/* placeholder structure for opaque set element backend representation. */ struct nft_elem_priv { };
@@ -287,6 +289,7 @@ struct nft_elem_priv { };* @key: element key * @key_end: closing element key * @data: element data +* @flags: flags * @priv: element private data and extensions */ struct nft_set_elem {
@@ -302,6 +305,7 @@ struct nft_set_elem {u32 buf[NFT_DATA_VALUE_MAXLEN /sizeof (u32)];struct nft_data val; } data; +u32 flags;struct nft_elem_priv *priv; };
net/netfilter/nf_tables_api.c +17 -4
@@ -7269,7 +7269,8 @@ static u32 nft_set_maxsize(const struct nft_set *set) }static int nft_add_set_elem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr,u32 nlmsg_flags) +const struct nlattr *attr,u32 nlmsg_flags, +bool last) {struct nft_expr *expr_array[NFT_SET_EXPR_MAX ] = {};struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];
@@ -7555,6 +7556,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; +if (obj) *nft_set_ext_obj (ext) = obj;
@@ -7718,7 +7724,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,nft_ctx_init (&ctx, net, skb, info->nlh, family, table,NULL , nla);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags); + err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags, +nla_is_last (attr, rem));if (err <0 ) {NL_SET_BAD_ATTR (extack, attr);return err;
@@ -7841,7 +7848,7 @@ static void nft_trans_elems_destroy_abort(const struct nft_ctx *ctx, }static int nft_del_setelem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr) +const struct nlattr *attr,bool last) {struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];struct nft_set_ext_tmpl tmpl;
@@ -7909,6 +7916,11 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; + trans =nft_trans_elem_alloc (ctx,NFT_MSG_DELSETELEM , set);if (trans ==NULL ) goto fail_trans;
@@ -8056,7 +8068,8 @@ static int nf_tables_delsetelem(struct sk_buff *skb,return nft_set_flush (&ctx, set, genmask);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_del_setelem (&ctx, set, attr); + err =nft_del_setelem (&ctx, set, attr, +nla_is_last (attr, rem));if (err == -ENOENT &&NFNL_MSG_TYPE (info->nlh->nlmsg_type) ==NFT_MSG_DESTROYSETELEM )continue ;
net/netfilter/nft_set_rbtree.c +61 -10
136 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 420/781 PCI: rzg3s-host: Fix device node reference leak in rzg3s_pcie_host_parse_port()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
[ Upstream commit e43e2aa557040bbcc5de0eaa1c59ee3ae9e31793 ]
In rzg3s_pcie_host_parse_port(), of_get_next_child() returns a device node with an incremented reference count that must be released with of_node_put(). The current code fails to call of_node_put() which causes a reference leak.
Use the __free(device_node) attribute to ensure automatic cleanup when the variable goes out of scope.
--- drivers/pci/controller/pcie-rzg3s-host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/pci/controller/pcie-rzg3s-host.c +2 -1
@@ -1143,7 +1143,8 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host)static int rzg3s_pcie_host_parse_port (struct rzg3s_pcie_host *host) { -struct device_node *of_port =of_get_next_child (host->dev->of_node,NULL ); + struct device_node *of_port __free(device_node) = +of_get_next_child (host->dev->of_node,NULL );struct rzg3s_pcie_port *port = &host->port;int ret;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 421/781 PCI: Add ACS quirk for Pericom PI7C9X2G404 switches [12d8:b404]
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Cavallari <[email protected]>
[ Upstream commit 5907a90551e9f7968781f3a6ab8684458959beb3 ]
12d8:b404 is apparently another PCI ID for Pericom PI7C9X2G404 (as identified by the chip silkscreen and lspci).
It is also affected by the PI7C9X2G errata (e.g. a network card attached to it fails under load when P2P Redirect Request is enabled), so apply the same quirk to this PCI ID too.
PCI bridge [0604]: Pericom Semiconductor PI7C9X2G404 EV/SV PCIe2 4-Port/4-Lane Packet Switch [12d8:b404] (rev 01)
--- drivers/pci/quirks.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/quirks.c +4 -0
@@ -6189,6 +6189,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303, pci_fixup_pericom_acs_store_forward);DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0x2303 , pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE (PCI_VENDOR_ID_PERICOM ,0xb404 , + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0xb404 , + pci_fixup_pericom_acs_store_forward);static void nvidia_ion_ahci_fixup (struct pci_dev *pdev) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 422/781 rust: driver-core: use "kernel vertical" style for imports
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Danilo Krummrich <[email protected]>
[ Upstream commit 52563c665b0b0b39f319bee40ecc5e8f25b9050a ]
Convert all imports to use "kernel vertical" style.
With this, subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern.
While at it, drop unnecessary imports covered by prelude::*.
Stable-dep-of: ba268514ea14 ("rust: devres: fix race condition due to nesting")
--- rust/kernel/device.rs | 14 +++++++++++--- rust/kernel/devres.rs | 25 +++++++++++++++++++------ rust/kernel/driver.rs | 12 ++++++++---- 3 files changed, 38 insertions(+), 13 deletions(-)
rust/kernel/device.rs +11 -3
@@ -5,12 +5,20 @@//! C header: [`include/linux/device.h`](srctree/include/linux/device.h) use crate ::{ - bindings, fmt, + bindings, + fmt, prelude::*, sync::aref::ARef , - types::{ForeignOwnable ,Opaque }, + types::{ +ForeignOwnable , +Opaque ,// + },// +}; +use core::{ + any::TypeId , + marker::PhantomData , + ptr,// }; -use core::{any::TypeId , marker::PhantomData , ptr};#[cfg( CONFIG_PRINTK )] use crate ::c_str;
rust/kernel/devres.rs +19 -6
@@ -8,13 +8,26 @@use crate ::{ alloc::Flags , bindings, - device::{Bound ,Device }, - error::{to_result,Error ,Result }, - ffi::c_void, + device::{ +Bound , +Device ,// + }, + error::to_result, prelude::*, - revocable::{Revocable ,RevocableGuard }, - sync::{aref::ARef , rcu,Completion }, - types::{ForeignOwnable ,Opaque ,ScopeGuard }, + revocable::{ +Revocable , +RevocableGuard ,// + }, + sync::{ + aref::ARef , + rcu, +Completion ,// + }, + types::{ +ForeignOwnable , +Opaque , +ScopeGuard ,// + }, };use pin_init::Wrapper ;
rust/kernel/driver.rs +8 -4
@@ -94,10 +94,14 @@//! [`device_id`]: kernel::device_id //! [`module_driver`]: kernel::module_driver -use crate ::error::{Error ,Result }; -use crate ::{acpi, device, of, str::CStr , try_pin_init, types::Opaque ,ThisModule }; -use core::pin::Pin ; -use pin_init::{pin_data, pinned_drop,PinInit }; +use crate ::{ + acpi, + device, + of, + prelude::*, + types::Opaque , +ThisModule ,// +};/// Trait describing the layout of a specific device driver. ///
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 423/781 rust: devres: fix race condition due to nesting
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Danilo Krummrich <[email protected]>
[ Upstream commit ba268514ea14b44570030e8ed2aef92a38679e85 ]
Commit f5d3ef25d238 ("rust: devres: get rid of Devres' inner Arc") did attempt to optimize away the internal reference count of Devres.
However, without an internal reference count, we can't support cases where Devres is indirectly nested, resulting into a deadlock.
Such indirect nesting easily happens in the following way:
A registration object (which is guarded by devres) hold a reference count of an object that holds a device resource guarded by devres itself.
For instance a drm::Registration holds a reference of a drm::Device. The drm::Device itself holds a device resource in its private data.
When the drm::Registration is dropped by devres, and it happens that it did hold the last reference count of the drm::Device, it also drops the device resource, which is guarded by devres itself.
Thus, resulting into a deadlock in the Devres destructor of the device resource, as in the following backtrace.
sysrq: Show Blocked State task:rmmod state:D stack:0 pid:1331 tgid:1331 ppid:1330 task_flags:0x400100 flags:0x00000010 Call trace: __switch_to+0x190/0x294 (T) __schedule+0x878/0xf10 schedule+0x4c/0xcc schedule_timeout+0x44/0x118 wait_for_common+0xc0/0x18c wait_for_completion+0x18/0x24 _RINvNtCs4gKlGRWyJ5S_4core3ptr13drop_in_placeINtNtNtCsgzhNYVB7wSz_6kernel4sync3arc3ArcINtNtBN_6devres6DevresmEEECsRdyc7Hyps3_15rust_driver_pci+0x68/0xe8 [rust_driver_pci] _RINvNvNtCsgzhNYVB7wSz_6kernel6devres16register_foreign8callbackINtNtCs4gKlGRWyJ5S_4core3pin3PinINtNtNtB6_5alloc4kbox3BoxINtNtNtB6_4sync3arc3ArcINtB4_6DevresmEENtNtB1A_9allocator7KmallocEEECsRdyc7Hyps3_15rust_driver_pci+0x34/0xc8 [rust_driver_pci] devm_action_release+0x14/0x20 devres_release_all+0xb8/0x118 device_release_driver_internal+0x1c4/0x28c driver_detach+0x94/0xd4 bus_remove_driver+0xdc/0x11c driver_unregister+0x34/0x58 pci_unregister_driver+0x20/0x80 __arm64_sys_delete_module+0x1d8/0x254 invoke_syscall+0x40/0xcc el0_svc_common+0x8c/0xd8 do_el0_svc+0x1c/0x28 el0_svc+0x54/0x1d4 el0t_64_sync_handler+0x84/0x12c el0t_64_sync+0x198/0x19c
In order to fix this, re-introduce the internal reference count.
--- rust/kernel/devres.rs | 149 ++++++++++++------------------------------ 1 file changed, 40 insertions(+), 109 deletions(-)
rust/kernel/devres.rs +40 -109
@@ -21,30 +21,11 @@ sync::{ aref::ARef, rcu, - Completion, // - }, - types::{ -ForeignOwnable , -Opaque , -ScopeGuard ,// + Arc, // }, + types::ForeignOwnable, }; -use pin_init::Wrapper ; - -/// [`Devres`] inner data accessed from [`Devres::callback`]. -#[pin_data] -struct Inner <T :Send > { -#[pin] - data:Revocable <T >, -/// Tracks whether [`Devres::callback`] has been completed. -#[pin] - devm:Completion , -/// Tracks whether revoking [`Self::data`] has been completed. -#[pin] - revoke:Completion , -} -/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to /// manage their lifetime. ///
@@ -118,18 +99,13 @@ struct Inner<T: Send> {/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? }; -/// let devres = KBox::pin_init(Devres::new(dev, iomem), GFP_KERNEL)?; +/// let devres = Devres::new(dev, iomem)?; /// /// let res = devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); /// # Ok(()) /// # } /// ``` -/// -/// # Invariants -/// -/// `Self::inner` is guaranteed to be initialized and is always accessed read-only. -#[pin_data( PinnedDrop )] pub struct Devres <T :Send > { dev:ARef <Device >,/// Pointer to [`Self::devres_callback`].
@@ -137,14 +113,7 @@ pub struct Devres<T: Send> {/// Has to be stored, since Rust does not guarantee to always return the same address for a /// function. However, the C API uses the address as a key. callback: unsafe extern "C" fn(*mut c_void), -/// Contains all the fields shared with [`Self::callback`]. -// TODO: Replace with `UnsafePinned`, once available. -// -// Subsequently, the `drop_in_place()` in `Devres::drop` and `Devres::new` as well as the -// explicit `Send` and `Sync' impls can be removed. -#[pin] - inner: Opaque<Inner<T>>, - _add_action: (), + data: Arc<Revocable<T>>, }impl <T :Send >Devres <T > {
@@ -152,74 +121,48 @@ impl<T: Send> Devres<T> {/// /// The `data` encapsulated within the returned `Devres` instance' `data` will be /// (revoked)[`Revocable`] once the device is detached. -pub fn new <'a,E >( - dev: &'aDevice <Bound >, - data:impl PinInit <T ,E > + 'a, - ) ->impl PinInit <Self ,Error > + 'a +pub fn new <E >(dev: &Device <Bound >, data:impl PinInit <T ,E >) ->Result <Self >where -T : 'a,Error :From <E >, { -try_pin_init! (&this inSelf { - dev: dev.into (), - callback:Self ::devres_callback, -// INVARIANT: `inner` is properly initialized. - inner <-Opaque ::pin_init (try_pin_init! (Inner { - devm <-Completion ::new (), - revoke <-Completion ::new (), - data <-Revocable ::new (data), - })), -// TODO: Replace with "initializer code blocks" [1] once available. -// -// [1] https://github.com/Rust-for-Linux/pin-init/pull/69 - _add_action: { -// SAFETY: `this` is a valid pointer to uninitialized memory. -let inner =unsafe { &raw mut (*this.as_ptr ()).inner }; +let callback =Self ::devres_callback; +let data =Arc ::pin_init (Revocable ::new (data),GFP_KERNEL )?; +let devres_data = data.clone (); -// SAFETY: -// - `dev.as_raw()` is a pointer to a valid bound device. -// - `inner` is guaranteed to be a valid for the duration of the lifetime of `Self`. -// - `devm_add_action()` is guaranteed not to call `callback` until `this` has been -// properly initialized, because we require `dev` (i.e. the *bound* device) to -// live at least as long as the returned `impl PinInit<Self, Error>`. -to_result (unsafe { - bindings::devm_add_action (dev.as_raw (),Some (*callback), inner.cast ()) - }).inspect_err (|_| { -let inner =Opaque ::cast_into (inner); +// SAFETY: +// - `dev.as_raw()` is a pointer to a valid bound device. +// - `data` is guaranteed to be a valid for the duration of the lifetime of `Self`. +// - `devm_add_action()` is guaranteed not to call `callback` for the entire lifetime of +// `dev`. +to_result (unsafe { + bindings::devm_add_action ( + dev.as_raw (), +Some (callback), +Arc ::as_ptr (&data).cast_mut ().cast (), + ) + })?; -// SAFETY: `inner` is a valid pointer to an `Inner<T>` and valid for both reads -// and writes. -unsafe { core::ptr::drop_in_place (inner) }; - })?; - }, - }) - } +// `devm_add_action()` was successful and has consumed the reference count. + core::mem::forget (devres_data); -fn inner (&self) -> &Inner <T > { -// SAFETY: By the type invairants of `Self`, `inner` is properly initialized and always -// accessed read-only. -unsafe { &*self.inner.get () } +Ok (Self { + dev: dev.into (), + callback, + data, + }) }fn data (&self) -> &Revocable <T > { - &self.inner ().data + &self.data }#[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback (ptr: *mut kernel::ffi::c_void ) { -// SAFETY: In `Self::new` we've passed a valid pointer to `Inner` to `devm_add_action()`, -// hence `ptr` must be a valid pointer to `Inner`. -let inner =unsafe { &*ptr.cast ::<Inner <T >>() }; +// SAFETY: In `Self::new` we've passed a valid pointer of `Revocable<T>` to +// `devm_add_action()`, hence `ptr` must be a valid pointer to `Revocable<T>`. +let data =unsafe {Arc ::from_raw (ptr.cast ::<Revocable <T >>()) }; -// Ensure that `inner` can't be used anymore after we signal completion of this callback. -let inner =ScopeGuard ::new_with_data (inner, |inner| inner.devm.complete_all ()); - -if !inner.data.revoke () { -// If `revoke()` returns false, it means that `Devres::drop` already started revoking -// `data` for us. Hence we have to wait until `Devres::drop` signals that it -// completed revoking `data`. - inner.revoke.wait_for_completion (); - } + data.revoke (); }fn remove_action (&self) ->bool {
@@ -231,7 +174,7 @@ fn remove_action(&self) -> bool { bindings::devm_remove_action_nowarn ( self.dev.as_raw (),Some (self.callback), - core::ptr::from_ref (self.inner ()).cast_mut ().cast (), + core::ptr::from_ref (self.data ()).cast_mut ().cast (), ) } == 0) }
@@ -302,31 +245,19 @@ unsafe impl<T: Send> Send for Devres<T> {}// SAFETY: `Devres` can be shared with any task, if `T: Sync`. unsafe impl <T :Send +Sync >Sync for Devres <T > {} -#[pinned_drop] -impl <T :Send >PinnedDrop for Devres <T > { -fn drop (self:Pin <&mut Self >) { +impl <T :Send >Drop for Devres <T > { +fn drop (&mut self) {// SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. if unsafe { self.data ().revoke_nosync () } {// We revoked `self.data` before the devres action did, hence try to remove it. -if !self.remove_action () { -// We could not remove the devres action, which means that it now runs concurrently, -// hence signal that `self.data` has been revoked by us successfully. - self.inner ().revoke.complete_all (); - -// Wait for `Self::devres_callback` to be done using this object. - self.inner ().devm.wait_for_completion (); +if self.remove_action () { +// SAFETY: In `Self::new` we have taken an additional reference count of `self.data` +// for `devm_add_action()`. Since `remove_action()` was successful, we have to drop +// this additional reference count. +drop (unsafe {Arc ::from_raw (Arc ::as_ptr (&self.data)) }); } - }else { -// `Self::devres_callback` revokes `self.data` for us, hence wait for it to be done -// using this object. - self.inner ().devm.wait_for_completion (); } - -// INVARIANT: At this point it is guaranteed that `inner` can't be accessed any more. -// -// SAFETY: `inner` is valid for dropping. -unsafe { core::ptr::drop_in_place (self.inner.get ()) }; } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 424/781 dpll: zl3073x: Fix output pin phase adjustment sign
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 5d41f95f5d0bd9db02f3f16a649d0631f71e9fdb ]
The output pin phase adjustment functions incorrectly negate the phase compensation value.
Per the ZL3073x datasheet, the output phase compensation register is simply a signed two's complement integer where:
- Positive values move the phase later in time
- Negative values move the phase earlier in time
No negation is required. The erroneous negation caused phase adjustments to be applied in the wrong direction.
Note that input pin phase adjustment correctly uses negation because the hardware has an inverted convention for input references (positive moves phase earlier, negative moves phase later).
--- drivers/dpll/zl3073x/dpll.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
drivers/dpll/zl3073x/dpll.c +4 -8
@@ -1039,10 +1039,8 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, out_id =zl3073x_output_pin_out_get (pin->id); out =zl3073x_out_state_get (zldev, out_id); -/* Convert value to ps and reverse two's complement negation applied -* during 'set' -*/ - *phase_adjust = -out->phase_comp * pin->phase_gran; +/* The value in the register is expressed in half synth clock cycles. */ + *phase_adjust = out->phase_comp * pin->phase_gran;return 0 ; }
@@ -1064,10 +1062,8 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, out_id =zl3073x_output_pin_out_get (pin->id); out = *zl3073x_out_state_get (zldev, out_id); -/* The value in the register is stored as two's complement negation -* of requested value and expressed in half synth clock cycles. -*/ - out.phase_comp = -phase_adjust / pin->phase_gran; +/* The value in the register is expressed in half synth clock cycles. */ + out.phase_comp = phase_adjust / pin->phase_gran;/* Update output configuration from mailbox */ return zl3073x_out_state_set (zldev, out_id, &out);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 425/781 net: hns3: fix double free issue for tx spare buffer
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jian Shen <[email protected]>
[ Upstream commit 6d2f142b1e4b203387a92519d9d2e34752a79dbb ]
In hns3_set_ringparam(), a temporary copy (tmp_rings) of the ring structure is created for rollback. However, the tx_spare pointer in the original ring handle is incorrectly left pointing to the old backup memory.
Later, if memory allocation fails in hns3_init_all_ring() during the setup, the error path attempts to free all newly allocated rings. Since tx_spare contains a stale (non-NULL) pointer from the backup, it is mistaken for a newly allocated buffer and is erroneously freed, leading to a double-free of the backup memory.
The root cause is that the tx_spare field was not cleared after its value was saved in tmp_rings, leaving a dangling pointer.
Fix this by setting tx_spare to NULL in the original ring structure when the creation of the new tx_spare fails. This ensures the error cleanup path only frees genuinely newly allocated buffers.
--- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +9 -2
@@ -1048,13 +1048,13 @@ static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring)int order;if (!alloc_size) -return ; + goto not_init; order =get_order (alloc_size);if (order >MAX_PAGE_ORDER ) {if (net_ratelimit ())dev_warn (ring_to_dev (ring),"failed to allocate tx spare buffer, exceed to max order\n" ); -return ; + goto not_init; } tx_spare = devm_kzalloc(ring_to_dev(ring), sizeof(*tx_spare),
@@ -1092,6 +1092,13 @@ static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring)devm_kfree (ring_to_dev (ring), tx_spare); devm_kzalloc_error: ring->tqp->handle->kinfo.tx_spare_buf_size =0 ; +not_init: +/* When driver init or reset_init, the ring->tx_spare is always NULL; +* but when called from hns3_set_ringparam, it's usually not NULL, and +* will be restored if hns3_init_all_ring() failed. So it's safe to set +* ring->tx_spare to NULL here. +*/ + ring->tx_spare =NULL ; }/* Use hns3_tx_spare_space() to make sure there is enough buffer
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 426/781 procfs: fix missing RCU protection when reading real_parent in do_task_stat()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jinliang Zheng <[email protected]>
[ Upstream commit 76149d53502cf17ef3ae454ff384551236fba867 ]
When reading /proc/[pid]/stat, do_task_stat() accesses task->real_parent without proper RCU protection, which leads to:
cpu 0 cpu 1
----- -----
do_task_stat
var = task->real_parent
release_task
call_rcu(delayed_put_task_struct)
task_tgid_nr_ns(var)
rcu_read_lock <--- Too late to protect task->real_parent!
task_pid_ptr <--- UAF!
rcu_read_unlockThis patch uses task_ppid_nr_ns() instead of task_tgid_nr_ns() to add proper RCU protection for accessing task->real_parent.
--- fs/proc/array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/proc/array.c +1 -1
@@ -528,7 +528,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } sid =task_session_nr_ns (task, ns); - ppid =task_tgid_nr_ns (task->real_parent, ns); + ppid =task_ppid_nr_ns (task, ns); pgid =task_pgrp_nr_ns (task, ns);unlock_task_sighand (task, &flags);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 427/781 smb: client: correct value for smbd_max_fragmented_recv_size
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Metzmacher <[email protected]>
[ Upstream commit 4a93d1ee2d0206970b6eb13fbffe07938cd95948 ]
When we download a file without rdma offload or get
a large directly enumeration from the server,
the server might want to send up to smbd_max_fragmented_recv_size bytes, but if it is too large all our recv buffers might already be moved to the recv_io.reassembly.list and we're no longer able to grant recv credits.
The maximum fragmented upper-layer payload receive size supported
Assume max_payload_per_credit issmbd_max_receive_size - 24 = 1340
The maximum number would besmbd_receive_credit_max * max_payload_per_credit
1340 * 255 = 341700 (0x536C4)
The minimum value from the spec is 131072 (0x20000)
For now we use the logic we used in ksmbd before:
(1364 * 255) / 2 = 173910 (0x2A756)
--- fs/smb/client/smbdirect.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
fs/smb/client/smbdirect.c +17 -2
@@ -101,8 +101,23 @@ int smbd_send_credit_target = 255;/* The maximum single message size can be sent to remote peer */ int smbd_max_send_size =1364 ; -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size =1024 *1024 ; +/* +* The maximum fragmented upper-layer payload receive size supported +* +* Assume max_payload_per_credit is +* smbd_max_receive_size - 24 = 1340 +* +* The maximum number would be +* smbd_receive_credit_max * max_payload_per_credit +* +* 1340 * 255 = 341700 (0x536C4) +* +* The minimum value from the spec is 131072 (0x20000) +* +* For now we use the logic we used in ksmbd before: +* (1364 * 255) / 2 = 173910 (0x2A756) +*/ +int smbd_max_fragmented_recv_size = (1364 *255 ) /2 ;/* The maximum single-message size which can be received */ int smbd_max_receive_size =1364 ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 428/781 net: atm: fix crash due to unvalidated vcc pointer in sigd_send()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit ae88a5d2f29b69819dc7b04086734439d074a643 ]
Reproducer available at [1].
The ATM send path (sendmsg -> vcc_sendmsg -> sigd_send) reads the vcc pointer from msg->vcc and uses it directly without any validation. This pointer comes from userspace via sendmsg() and can be arbitrarily forged:
int fd = socket(AF_ATMSVC, SOCK_DGRAM, 0);
ioctl(fd, ATMSIGD_CTRL); // become ATM signaling daemon
struct msghdr msg = { .msg_iov = &iov, ... };
*(unsigned long *)(buf + 4) = 0xdeadbeef; // fake vcc pointer
sendmsg(fd, &msg, 0); // kernel dereferences 0xdeadbeefIn normal operation, the kernel sends the vcc pointer to the signaling daemon via sigd_enq() when processing operations like connect(), bind(), or listen(). The daemon is expected to return the same pointer when responding. However, a malicious daemon can send arbitrary pointer values.
Fix this by introducing find_get_vcc() which validates the pointer by searching through vcc_hash (similar to how sigd_close() iterates over all VCCs), and acquires a reference via sock_hold() if found.
Since struct atm_vcc embeds struct sock as its first member, they share the same lifetime. Therefore using sock_hold/sock_put is sufficient to keep the vcc alive while it is being used.
Note that there may be a race with sigd_close() which could mark the vcc with various flags (e.g., ATM_VF_RELEASED) after find_get_vcc() returns. However, sock_hold() guarantees the memory remains valid, so this race only affects the logical state, not memory safety.
[1]: https://gist.github.com/mrpre/1ba5949c45529c511152e2f4c755b0f3
--- net/atm/signaling.c | 56 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-)
net/atm/signaling.c +54 -2
@@ -22,6 +22,36 @@struct atm_vcc *sigd =NULL ; +/* +* find_get_vcc - validate and get a reference to a vcc pointer +* @vcc: the vcc pointer to validate +* +* This function validates that @vcc points to a registered VCC in vcc_hash. +* If found, it increments the socket reference count and returns the vcc. +* The caller must call sock_put(sk_atm(vcc)) when done. +* +* Returns the vcc pointer if valid, NULL otherwise. +*/ +static struct atm_vcc *find_get_vcc (struct atm_vcc *vcc) +{ +int i; + +read_lock (&vcc_sklist_lock); +for (i =0 ; i <VCC_HTABLE_SIZE ; i++) { +struct sock *s; + +sk_for_each (s, &vcc_hash[i]) { +if (atm_sk (s) == vcc) { +sock_hold (s); +read_unlock (&vcc_sklist_lock); +return vcc; + } + } + } +read_unlock (&vcc_sklist_lock); +return NULL ; +} +static void sigd_put_skb (struct sk_buff *skb) {if (!sigd) {
@@ -69,7 +99,14 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) msg = (struct atmsvc_msg *) skb->data;WARN_ON (refcount_sub_and_test (skb->truesize, &sk_atm (vcc)->sk_wmem_alloc)); - vcc = *(struct atm_vcc **) &msg->vcc; + + vcc =find_get_vcc (*(struct atm_vcc **)&msg->vcc); +if (!vcc) { +pr_debug ("invalid vcc pointer in msg\n" ); +dev_kfree_skb (skb); +return -EINVAL ; + } +pr_debug ("%d (0x%lx)\n" , (int )msg->type, (unsigned long )vcc); sk =sk_atm (vcc);
@@ -100,7 +137,16 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)clear_bit (ATM_VF_WAITING , &vcc->flags);break ;case as_indicate: - vcc = *(struct atm_vcc **)&msg->listen_vcc; +/* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */ +sock_put (sk); + + vcc =find_get_vcc (*(struct atm_vcc **)&msg->listen_vcc); +if (!vcc) { +pr_debug ("invalid listen_vcc pointer in msg\n" ); +dev_kfree_skb (skb); +return -EINVAL ; + } + sk =sk_atm (vcc);pr_debug ("as_indicate!!!\n" );lock_sock (sk);
@@ -115,6 +161,8 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) sk->sk_state_change (sk); as_indicate_complete:release_sock (sk); +/* Paired with find_get_vcc(msg->listen_vcc) above */ +sock_put (sk);return 0 ;case as_close:set_bit (ATM_VF_RELEASED , &vcc->flags);
@@ -131,11 +179,15 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)break ;default :pr_alert ("bad message type %d\n" , (int )msg->type); +/* Paired with find_get_vcc(msg->vcc) above */ +sock_put (sk);return -EINVAL ; } sk->sk_state_change (sk); out:dev_kfree_skb (skb); +/* Paired with find_get_vcc(msg->vcc) above */ +sock_put (sk);return 0 ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 429/781 net: sunhme: Fix sbus regression
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: René Rebe <[email protected]>
[ Upstream commit 8c5d17834ec104d0abd1bda52fbc04e647fab274 ]
Commit cc216e4b44ce ("net: sunhme: Switch SBUS to devres") changed explicit sized of_ioremap with BMAC_REG_SIZEs to devm_platform_ioremap_resource mapping all the resource. However, this does not work on my Sun Ultra 2 with SBUS HMEs:
hme f0072f38: error -EBUSY: can't request region for resource [mem 0x1ffe8c07000-0x1ffe8c0701f] hme f0072f38: Cannot map TCVR registers.
hme f0072f38: probe with driver hme failed with error -16
hme f007ab44: error -EBUSY: can't request region for resource [mem 0x1ff28c07000-0x1ff28c0701f] hme f007ab44: Cannot map TCVR registers.
hme f007ab44: probe with driver hme failed with error -16
Turns out the open-firmware resources overlap, at least on this machines and PROM version:
hexdump /proc/device-tree/sbus@1f,0/SUNW,hme@2,8c00000/reg:
00 00 00 02 08 c0 00 00 00 00 01 08 00 00 00 02 08 c0 20 00 00 00 20 00 00 00 00 02 08 c0 40 00 00 00 20 00 00 00 00 02 08 c0 60 00 00 00 20 00 00 00 00 02 08 c0 70 00 00 00 00 20
And the driver previously explicitly mapped way smaller mmio regions:
/proc/iomem:
1ff28c00000-1ff28c00107 : HME Global Regs
1ff28c02000-1ff28c02033 : HME TX Regs
1ff28c04000-1ff28c0401f : HME RX Regs
1ff28c06000-1ff28c0635f : HME BIGMAC Regs
1ff28c07000-1ff28c0701f : HME Tranceiver Regs
Quirk this specific issue by truncating the previous resource to not overlap into the TCVR registers.
--- drivers/net/ethernet/sun/sunhme.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/sun/sunhme.c +3 -0
@@ -2551,6 +2551,9 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) goto err_out_clear_quattro; } +/* BIGMAC may have bogus sizes */ +if ((op->resource[3 ].end - op->resource[3 ].start) >=BMAC_REG_SIZE ) + op->resource[3 ].end = op->resource[3 ].start +BMAC_REG_SIZE -1 ; hp->bigmacregs =devm_platform_ioremap_resource (op,3 );if (IS_ERR (hp->bigmacregs)) {dev_err (&op->dev,"Cannot map BIGMAC registers.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 430/781 xfrm: fix ip_rt_bug race in icmp_route_lookup reverse path
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 81b84de32bb27ae1ae2eb9acf0420e9d0d14bf00 ]
icmp_route_lookup() performs multiple route lookups to find a suitable route for sending ICMP error messages, with special handling for XFRM (IPsec) policies.
The lookup sequence is:
1. First, lookup output route for ICMP reply (dst = original src)
2. Pass through xfrm_lookup() for policy check
3. If blocked (-EPERM) or dst is not local, enter "reverse path"
4. In reverse path, call xfrm_decode_session_reverse() to get fl4_dec which reverses the original packet's flow (saddr<->daddr swapped)
5. If fl4_dec.saddr is local (we are the original destination), use __ip_route_output_key() for output route lookup
6. If fl4_dec.saddr is NOT local (we are a forwarding node), use ip_route_input() to simulate the reverse packet's input path
7. Finally, pass rt2 through xfrm_lookup() with XFRM_LOOKUP_ICMP flag
The bug occurs in step 6: ip_route_input() is called with fl4_dec.daddr (original packet's source) as destination. If this address becomes local between the initial check and ip_route_input() call (e.g., due to concurrent "ip addr add"), ip_route_input() returns a LOCAL route with dst.output set to ip_rt_bug.
This route is then used for ICMP output, causing dst_output() to call ip_rt_bug(), triggering a WARN_ON:
------------[ cut here ]------------ WARNING: net/ipv4/route.c:1275 at ip_rt_bug+0x21/0x30, CPU#1 Call Trace: <TASK> ip_push_pending_frames+0x202/0x240 icmp_push_reply+0x30d/0x430 __icmp_send+0x1149/0x24f0 ip_options_compile+0xa2/0xd0 ip_rcv_finish_core+0x829/0x1950 ip_rcv+0x2d7/0x420 __netif_receive_skb_one_core+0x185/0x1f0 netif_receive_skb+0x90/0x450 tun_get_user+0x3413/0x3fb0 tun_chr_write_iter+0xe4/0x220 ...
Fix this by checking rt2->rt_type after ip_route_input(). If it's RTN_LOCAL, the route cannot be used for output, so treat it as an error.
The reproducer requires kernel modification to widen the race window, making it unsuitable as a selftest. It is available at:
https://gist.github.com/mrpre/eae853b72ac6a750f5d45d64ddac1e81
--- net/ipv4/icmp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
net/ipv4/icmp.c +15 -0
@@ -554,6 +554,21 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,/* steal dst entry from skb_in, don't drop refcnt */ skb_dstref_steal (skb_in);skb_dstref_restore (skb_in, orefdst); + +/* +* At this point, fl4_dec.daddr should NOT be local (we +* checked fl4_dec.saddr above). However, a race condition +* may occur if the address is added to the interface +* concurrently. In that case, ip_route_input() returns a +* LOCAL route with dst.output=ip_rt_bug, which must not +* be used for output. +*/ +if (!err && rt2 && rt2->rt_type ==RTN_LOCAL ) { +net_warn_ratelimited ("detected local route for %pI4 during ICMP sending, src %pI4\n" , + &fl4_dec.daddr, &fl4_dec.saddr); +dst_release (&rt2->dst); + err = -EINVAL ; + } }if (err)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 431/781 serial: caif: fix use-after-free in caif_serial ldisc_close()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 308e7e4d0a846359685f40aade023aee7b27284c ]
There is a use-after-free bug in caif_serial where handle_tx() may access ser->tty after the tty has been freed.
The race condition occurs between ldisc_close() and packet transmission:
CPU 0 (close) CPU 1 (xmit)
------------- ------------
ldisc_close()
tty_kref_put(ser->tty)
[tty may be freed here]
<-- race window -->
caif_xmit()
handle_tx()
tty = ser->tty // dangling ptr
tty->ops->write() // UAF!
schedule_work()
ser_release()
unregister_netdevice()The root cause is that tty_kref_put() is called in ldisc_close() while the network device is still active and can receive packets.
Since ser and tty have a 1:1 binding relationship with consistent lifecycles (ser is allocated in ldisc_open and freed in ser_release via unregister_netdevice, and each ser binds exactly one tty), we can safely defer the tty reference release to ser_release() where the network device is unregistered.
Fix this by moving tty_kref_put() from ldisc_close() to ser_release(), after unregister_netdevice(). This ensures the tty reference is held as long as the network device exists, preventing the UAF.
Note: We save ser->tty before unregister_netdevice() because ser is embedded in netdev's private data and will be freed along with netdev (needs_free_netdev = true).
How to reproduce: Add mdelay(500) at the beginning of ldisc_close() to widen the race window, then run the reproducer program [1].
Note: There is a separate deadloop issue in handle_tx() when using PORT_UNKNOWN serial ports (e.g., /dev/ttyS3 in QEMU without proper serial backend). This deadloop exists even without this patch, and is likely caused by inconsistency between uart_write_room() and uart_write() in serial core. It has been addressed in a separate patch [2].
KASAN report:
==================================================================
BUG: KASAN: slab-use-after-free in handle_tx+0x5d1/0x620 Read of size 1 at addr ffff8881131e1490 by task caif_uaf_trigge/9929 Call Trace: <TASK> dump_stack_lvl+0x10e/0x1f0 print_report+0xd0/0x630 kasan_report+0xe4/0x120 handle_tx+0x5d1/0x620 dev_hard_start_xmit+0x9d/0x6c0 __dev_queue_xmit+0x6e2/0x4410 packet_xmit+0x243/0x360 packet_sendmsg+0x26cf/0x5500 __sys_sendto+0x4a3/0x520 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0xc9/0xf80 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f615df2c0d7 Allocated by task 9930: Freed by task 64: Last potentially related work creation: The buggy address belongs to the object at ffff8881131e1000 which belongs to the cache kmalloc-cg-2k of size 2048
The buggy address is located 1168 bytes inside of
freed 2048-byte region [ffff8881131e1000, ffff8881131e1800)
The buggy address belongs to the physical page:
page_owner tracks the page as allocated
page last free pid 9778 tgid 9778 stack trace:
Memory state around the buggy address:
ffff8881131e1380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8881131e1400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff8881131e1480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8881131e1500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8881131e1580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================[1]: https://gist.github.com/mrpre/f683f244544f7b11e7fa87df9e6c2eeb
[2]: https://lore.kernel.org/linux-serial/[email protected]/T/#u
--- drivers/net/caif/caif_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/caif/caif_serial.c +3 -2
@@ -284,6 +284,7 @@ static void ser_release(struct work_struct *work) {struct list_head list;struct ser_device *ser, *tmp; +struct tty_struct *tty;spin_lock (&ser_lock);list_replace_init (&ser_release_list, &list);
@@ -292,9 +293,11 @@ static void ser_release(struct work_struct *work)if (!list_empty (&list)) {rtnl_lock ();list_for_each_entry_safe (ser, tmp, &list, node) { + tty = ser->tty;dev_close (ser->dev);unregister_netdevice (ser->dev);debugfs_deinit (ser); +tty_kref_put (tty); }rtnl_unlock (); }
@@ -355,8 +358,6 @@ static void ldisc_close(struct tty_struct *tty) {struct ser_device *ser = tty->disc_data; -tty_kref_put (ser->tty); -spin_lock (&ser_lock);list_move (&ser->node, &ser_release_list);spin_unlock (&ser_lock);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 432/781 octeon_ep: disable per ring interrupts
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit 73e6ffa37cebee152c07c5f2b8bc70fd2899ea6e ]
Disable the MSI-X per ring interrupt for every PF ring when PF netdev goes down.
--- .../ethernet/marvell/octeon_ep/octep_cn9k_pf.c | 18 +++++++++++++++--- .../ethernet/marvell/octeon_ep/octep_cnxk_pf.c | 18 +++++++++++++++--- .../marvell/octeon_ep/octep_regs_cn9k_pf.h | 1 + .../marvell/octeon_ep/octep_regs_cnxk_pf.h | 1 + 4 files changed, 32 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +15 -3
@@ -696,14 +696,26 @@ static void octep_enable_interrupts_cn93_pf(struct octep_device *oct)/* Disable all interrupts */ static void octep_disable_interrupts_cn93_pf (struct octep_device *oct) { -u64 intr_mask =0ULL ; +u64 reg_val, intr_mask =0ULL ;int srn, num_rings, i; srn =CFG_GET_PORTS_PF_SRN (oct->conf); num_rings =CFG_GET_PORTS_ACTIVE_IO_RINGS (oct->conf); -for (i =0 ; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); +for (i =0 ; i < num_rings; i++) { + intr_mask |=BIT_ULL (srn + i); + reg_val =octep_read_csr64 (oct, +CN93_SDP_R_IN_INT_LEVELS (srn + i)); + reg_val &= ~CN93_INT_ENA_BIT ; +octep_write_csr64 (oct, +CN93_SDP_R_IN_INT_LEVELS (srn + i), reg_val); + + reg_val =octep_read_csr64 (oct, +CN93_SDP_R_OUT_INT_LEVELS (srn + i)); + reg_val &= ~CN93_INT_ENA_BIT ; +octep_write_csr64 (oct, +CN93_SDP_R_OUT_INT_LEVELS (srn + i), reg_val); + }octep_write_csr64 (oct,CN93_SDP_EPF_IRERR_RINT_ENA_W1C , intr_mask);octep_write_csr64 (oct,CN93_SDP_EPF_ORERR_RINT_ENA_W1C , intr_mask);
drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +15 -3
@@ -720,14 +720,26 @@ static void octep_enable_interrupts_cnxk_pf(struct octep_device *oct)/* Disable all interrupts */ static void octep_disable_interrupts_cnxk_pf (struct octep_device *oct) { -u64 intr_mask =0ULL ; +u64 reg_val, intr_mask =0ULL ;int srn, num_rings, i; srn =CFG_GET_PORTS_PF_SRN (oct->conf); num_rings =CFG_GET_PORTS_ACTIVE_IO_RINGS (oct->conf); -for (i =0 ; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); +for (i =0 ; i < num_rings; i++) { + intr_mask |=BIT_ULL (srn + i); + reg_val =octep_read_csr64 (oct, +CNXK_SDP_R_IN_INT_LEVELS (srn + i)); + reg_val &= ~CNXK_INT_ENA_BIT ; +octep_write_csr64 (oct, +CNXK_SDP_R_IN_INT_LEVELS (srn + i), reg_val); + + reg_val =octep_read_csr64 (oct, +CNXK_SDP_R_OUT_INT_LEVELS (srn + i)); + reg_val &= ~CNXK_INT_ENA_BIT ; +octep_write_csr64 (oct, +CNXK_SDP_R_OUT_INT_LEVELS (srn + i), reg_val); + }octep_write_csr64 (oct,CNXK_SDP_EPF_IRERR_RINT_ENA_W1C , intr_mask);octep_write_csr64 (oct,CNXK_SDP_EPF_ORERR_RINT_ENA_W1C , intr_mask);
drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h +1 -0
@@ -386,5 +386,6 @@#define CN93_PEM_BAR4_INDEX 7#define CN93_PEM_BAR4_INDEX_SIZE 0x400000ULL#define CN93_PEM_BAR4_INDEX_OFFSET (CN93_PEM_BAR4_INDEX * CN93_PEM_BAR4_INDEX_SIZE) +#define CN93_INT_ENA_BIT BIT_ULL(62)#endif /* _OCTEP_REGS_CN9K_PF_H_ */
drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h +1 -0
@@ -412,5 +412,6 @@#define CNXK_PEM_BAR4_INDEX 7#define CNXK_PEM_BAR4_INDEX_SIZE 0x400000ULL#define CNXK_PEM_BAR4_INDEX_OFFSET (CNXK_PEM_BAR4_INDEX * CNXK_PEM_BAR4_INDEX_SIZE) +#define CNXK_INT_ENA_BIT BIT_ULL(62)#endif /* _OCTEP_REGS_CNXK_PF_H_ */
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 433/781 octeon_ep: ensure dbell BADDR updation
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit ce8fe3fc4f99efd872120301c0f72f2e90ab9769 ]
Make sure the OUT DBELL base address reflects the
latest values written to it.
Fix:
Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures.
--- .../marvell/octeon_ep/octep_cn9k_pf.c | 3 +- .../marvell/octeon_ep/octep_cnxk_pf.c | 46 +++++++++++++++---- .../ethernet/marvell/octeon_ep/octep_main.h | 2 +- .../net/ethernet/marvell/octeon_ep/octep_rx.c | 8 +++- 4 files changed, 48 insertions(+), 11 deletions(-)
drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +2 -1
@@ -307,7 +307,7 @@ static void octep_setup_iq_regs_cn93_pf(struct octep_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_setup_oq_regs_cn93_pf (struct octep_device *oct,int oq_no) +static int octep_setup_oq_regs_cn93_pf (struct octep_device *oct,int oq_no) {u64 reg_val;u64 oq_ctl =0ULL ;
@@ -355,6 +355,7 @@ static void octep_setup_oq_regs_cn93_pf(struct octep_device *oct, int oq_no) reg_val = ((u64 )time_threshold <<32 ) |CFG_GET_OQ_INTR_PKT (oct->conf);octep_write_csr64 (oct,CN93_SDP_R_OUT_INT_LEVELS (oq_no), reg_val); +return 0 ; }/* Setup registers for a PF mailbox */
drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +38 -8
78 lines not on this page · raw message
drivers/net/ethernet/marvell/octeon_ep/octep_main.h +1 -1
@@ -77,7 +77,7 @@ struct octep_pci_win_regs {struct octep_hw_ops {void (*setup_iq_regs)(struct octep_device *oct,int q); -void (*setup_oq_regs)(struct octep_device *oct,int q); +int (*setup_oq_regs)(struct octep_device *oct,int q);void (*setup_mbox_regs)(struct octep_device *oct,int mbox);irqreturn_t (*mbox_intr_handler)(void *ioq_vector);
drivers/net/ethernet/marvell/octeon_ep/octep_rx.c +7 -1
@@ -12,6 +12,8 @@#include "octep_config.h" #include "octep_main.h" +static void octep_oq_free_ring_buffers (struct octep_oq *oq); +static void octep_oq_reset_indices (struct octep_oq *oq) { oq->host_read_idx =0 ;
@@ -170,11 +172,15 @@ static int octep_setup_oq(struct octep_device *oct, int q_no) goto oq_fill_buff_err;octep_oq_reset_indices (oq); - oct->hw_ops.setup_oq_regs (oct, q_no); +if (oct->hw_ops.setup_oq_regs (oct, q_no)) + goto oq_setup_err; + oct->num_oqs++;return 0 ; +oq_setup_err: +octep_oq_free_ring_buffers (oq); oq_fill_buff_err:vfree (oq->buff_info); oq->buff_info =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 434/781 octeon_ep_vf: ensure dbell BADDR updation
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit 484e834d53cffa91c311631271f83130cf6e9e7c ]
Make sure the OUT DBELL base address reflects the
latest values written to it.
Fix:
Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures.
--- .../marvell/octeon_ep_vf/octep_vf_cn9k.c | 3 +- .../marvell/octeon_ep_vf/octep_vf_cnxk.c | 39 +++++++++++++++++-- .../marvell/octeon_ep_vf/octep_vf_main.h | 2 +- .../marvell/octeon_ep_vf/octep_vf_rx.c | 8 +++- 4 files changed, 46 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c +2 -1
@@ -196,7 +196,7 @@ static void octep_vf_setup_iq_regs_cn93(struct octep_vf_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cn93 (struct octep_vf_device *oct,int oq_no) +static int octep_vf_setup_oq_regs_cn93 (struct octep_vf_device *oct,int oq_no) {struct octep_vf_oq *oq = oct->oq[oq_no];u32 time_threshold =0 ;
@@ -239,6 +239,7 @@ static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) time_threshold =CFG_GET_OQ_INTR_TIME (oct->conf); reg_val = ((u64 )time_threshold <<32 ) |CFG_GET_OQ_INTR_PKT (oct->conf);octep_vf_write_csr64 (oct,CN93_VF_SDP_R_OUT_INT_LEVELS (oq_no), reg_val); +return 0 ; }/* Setup registers for a VF mailbox */
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c +36 -3
@@ -199,11 +199,13 @@ static void octep_vf_setup_iq_regs_cnxk(struct octep_vf_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cnxk (struct octep_vf_device *oct,int oq_no) +static int octep_vf_setup_oq_regs_cnxk (struct octep_vf_device *oct,int oq_no) {struct octep_vf_oq *oq = oct->oq[oq_no]; +unsigned long t_out_jiffies;u32 time_threshold =0 ;u64 oq_ctl =ULL (0 ); +u64 reg_ba_val;u64 reg_val; reg_val =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no));
@@ -214,6 +216,38 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no)); }while (!(reg_val &CNXK_VF_R_OUT_CTL_IDLE )); } +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_WMARK (oq_no), + oq->max_count); +/* Wait for WMARK to get applied */ +usleep_range (10 ,15 ); + +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no), + oq->desc_ring_dma); +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_RSIZE (oq_no), + oq->max_count); + reg_ba_val =octep_vf_read_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no)); +if (reg_ba_val != oq->desc_ring_dma) { + t_out_jiffies = jiffies +10 *HZ ; +do { +if (reg_ba_val ==ULLONG_MAX ) +return -EFAULT ; +octep_vf_write_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no), oq->desc_ring_dma); +octep_vf_write_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_RSIZE + (oq_no), oq->max_count); + reg_ba_val = +octep_vf_read_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no)); + }while ((reg_ba_val != oq->desc_ring_dma) && +time_before (jiffies, t_out_jiffies)); + +if (reg_ba_val != oq->desc_ring_dma) +return -EAGAIN ; + } reg_val &= ~(CNXK_VF_R_OUT_CTL_IMODE ); reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_P );
@@ -227,8 +261,6 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val |= (CNXK_VF_R_OUT_CTL_ES_P );octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no), reg_val); -octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no), oq->desc_ring_dma); -octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_RSIZE (oq_no), oq->max_count); oq_ctl =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no));/* Clear the ISIZE and BSIZE (22-0) */
@@ -250,6 +282,7 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val &= ~GENMASK_ULL (31 ,0 ); reg_val |=CFG_GET_OQ_WMARK (oct->conf);octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_WMARK (oq_no), reg_val); +return 0 ; }/* Setup registers for a VF mailbox */
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h +1 -1
@@ -55,7 +55,7 @@ struct octep_vf_mmio {struct octep_vf_hw_ops {void (*setup_iq_regs)(struct octep_vf_device *oct,int q); -void (*setup_oq_regs)(struct octep_vf_device *oct,int q); +int (*setup_oq_regs)(struct octep_vf_device *oct,int q);void (*setup_mbox_regs)(struct octep_vf_device *oct,int mbox);irqreturn_t (*non_ioq_intr_handler)(void *ioq_vector);
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c +7 -1
@@ -12,6 +12,8 @@#include "octep_vf_config.h" #include "octep_vf_main.h" +static void octep_vf_oq_free_ring_buffers (struct octep_vf_oq *oq); +static void octep_vf_oq_reset_indices (struct octep_vf_oq *oq) { oq->host_read_idx =0 ;
@@ -171,11 +173,15 @@ static int octep_vf_setup_oq(struct octep_vf_device *oct, int q_no) goto oq_fill_buff_err;octep_vf_oq_reset_indices (oq); - oct->hw_ops.setup_oq_regs (oct, q_no); +if (oct->hw_ops.setup_oq_regs (oct, q_no)) + goto oq_setup_err; + oct->num_oqs++;return 0 ; +oq_setup_err: +octep_vf_oq_free_ring_buffers (oq); oq_fill_buff_err:vfree (oq->buff_info); oq->buff_info =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 435/781 ionic: Rate limit unknown xcvr type messages
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Joyner <[email protected]>
[ Upstream commit cdb1634de3bf197c0d86487d1fb84c128a79cc7c ]
Running ethtool repeatedly with a transceiver unknown to the driver or firmware will cause the driver to spam the kernel logs with "unknown xcvr type" messages which can distract from real issues; and this isn't interesting information outside of debugging. Fix this by rate limiting the output so that there are still notifications but not so many that they flood the log.
Using dev_dbg_once() would reduce the number of messages further, but this would miss the case where a different unknown transceiver type is plugged in, and its status is requested.
--- drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +4 -3
@@ -263,9 +263,10 @@ static int ionic_get_link_ksettings(struct net_device *netdev,/* This means there's no module plugged in */ break ;default : -dev_info (lif->ionic->dev,"unknown xcvr type pid=%d / 0x%x\n" , - idev->port_info->status.xcvr.pid, - idev->port_info->status.xcvr.pid); +dev_dbg_ratelimited (lif->ionic->dev, +"unknown xcvr type pid=%d / 0x%x\n" , + idev->port_info->status.xcvr.pid, + idev->port_info->status.xcvr.pid);break ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 436/781 net: renesas: rswitch: fix forwarding offload statemachine
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Dege <[email protected]>
[ Upstream commit e9a5073a98d940837cbb95e71eed1f28f48e7b30 ]
A change of the port state of one port, caused the state of another port to change. This behvior was unintended.
--- drivers/net/ethernet/renesas/rswitch_l2.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/net/ethernet/renesas/rswitch_l2.c +7 -8
@@ -1,7 +1,7 @@// SPDX-License-Identifier: GPL-2.0 /* Renesas Ethernet Switch device driver * -* Copyright (C) 2025 Renesas Electronics Corporation +* Copyright (C) 2025 - 2026 Renesas Electronics Corporation */ #include <linux/err.h>
@@ -60,6 +60,7 @@ static void rswitch_update_l2_hw_learning(struct rswitch_private *priv)static void rswitch_update_l2_hw_forwarding (struct rswitch_private *priv) {struct rswitch_device *rdev; +bool new_forwarding_offload;unsigned int fwd_mask;/* calculate fwd_mask with zeroes in bits corresponding to ports that
@@ -73,8 +74,9 @@ static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv) } rswitch_for_all_ports(priv, rdev) { -if ((rdev_for_l2_offload (rdev) && rdev->forwarding_requested) || - rdev->forwarding_offloaded) { + new_forwarding_offload = (rdev_for_l2_offload (rdev) && rdev->forwarding_requested); + +if (new_forwarding_offload || rdev->forwarding_offloaded) {/* Update allowed offload destinations even for ports * with L2 offload enabled earlier. *
@@ -84,13 +86,10 @@ static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv) priv->addr + FWPC2(rdev->port)); } -if (rdev_for_l2_offload (rdev) && - rdev->forwarding_requested && - !rdev->forwarding_offloaded) { +if (new_forwarding_offload && !rdev->forwarding_offloaded)rswitch_change_l2_hw_offloading (rdev, true, false); - }else if (rdev->forwarding_offloaded) { +else if (!new_forwarding_offload && rdev->forwarding_offloaded)rswitch_change_l2_hw_offloading (rdev, false, false); - } } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 437/781 octeontx2-pf: Unregister devlink on probe failure
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hariprasad Kelam <[email protected]>
[ Upstream commit 943f3b8bfbf297cf74392b50a7108ce1fe4cbd8c ]
When probe fails after devlink registration, the missing devlink unregister call causing a memory leak.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -0
@@ -3315,6 +3315,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_sriov_cleannup:otx2_sriov_vfcfg_cleanup (pf); err_pf_sriov_init: +otx2_unregister_dl (pf);otx2_shutdown_tc (pf); err_mcam_flow_del:otx2_mcam_flow_del (pf);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 438/781 af_unix: Fix memleak of newsk in unix_stream_connect().
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 6884028cd7f275f8bcb854a347265cb1fb0e4bea ]
When prepare_peercred() fails in unix_stream_connect(), unix_release_sock() is not called for newsk, and the memory is leaked.
Let's move prepare_peercred() before unix_create1().
--- net/unix/af_unix.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
net/unix/af_unix.c +3 -8
@@ -1650,10 +1650,9 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr_unsized *uad timeo =sock_sndtimeo (sk, flags &O_NONBLOCK ); -/* First of all allocate resources. -* If we will make it after state is locked, -* we will have to recheck all again in any case. -*/ + err =prepare_peercred (&peercred); +if (err) + goto out;/* create new sock for complete connection */ newsk =unix_create1 (net,NULL ,0 , sock->type);
@@ -1662,10 +1661,6 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr_unsized *uad goto out; } - err =prepare_peercred (&peercred); -if (err) - goto out; -/* Allocate skb for sending to listening sock */ skb =sock_wmalloc (newsk,1 ,0 ,GFP_KERNEL );if (!skb) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 439/781 RDMA/rtrs: server: remove dead code
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Honggang LI <[email protected]>
[ Upstream commit a3572bdc3a028ca47f77d7166ac95b719cf77d50 ]
As rkey had been initialized to zero, the WARN_ON_ONCE should never been triggered. Remove it.
--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/infiniband/ulp/rtrs/rtrs-srv.c +1 -7
@@ -208,7 +208,6 @@ static int rdma_write_sg(struct rtrs_srv_op *id)size_t sg_cnt;int err, offset;bool need_inval; -u32 rkey =0 ;struct ib_reg_wr rwr;struct ib_sge *plist;struct ib_sge list;
@@ -240,11 +239,6 @@ static int rdma_write_sg(struct rtrs_srv_op *id) wr->wr.num_sge =1 ; wr->remote_addr =le64_to_cpu (id->rd_msg->desc[0 ].addr); wr->rkey =le32_to_cpu (id->rd_msg->desc[0 ].key); -if (rkey ==0 ) - rkey = wr->rkey; -else -/* Only one key is actually used */ -WARN_ON_ONCE (rkey != wr->rkey); wr->wr.opcode =IB_WR_RDMA_WRITE ; wr->wr.wr_cqe = &io_comp_cqe;
@@ -277,7 +271,7 @@ static int rdma_write_sg(struct rtrs_srv_op *id) inv_wr.opcode =IB_WR_SEND_WITH_INV ; inv_wr.wr_cqe = &io_comp_cqe; inv_wr.send_flags =0 ; - inv_wr.ex.invalidate_rkey = rkey; + inv_wr.ex.invalidate_rkey = wr->rkey; } imm_wr.wr.next =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 440/781 IB/cache: update gid cache on client reregister event
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Etienne AUJAMES <[email protected]>
[ Upstream commit ddd6c8c873e912cb1ead79def54de5e24ff71c80 ]
Some HCAs (e.g: ConnectX4) do not trigger a IB_EVENT_GID_CHANGE on subnet prefix update from SM (PortInfo).
Since the commit d58c23c92548 ("IB/core: Only update PKEY and GID caches on respective events"), the GID cache is updated exclusively on IB_EVENT_GID_CHANGE. If this event is not emitted, the subnet prefix in the IPoIB interface’s hardware address remains set to its default value (0xfe80000000000000).
Then rdma_bind_addr() failed because it relies on hardware address to find the port GID (subnet_prefix + port GUID).
This patch fixes this issue by updating the GID cache on IB_EVENT_CLIENT_REREGISTER event (emitted on PortInfo::ClientReregister=1).
--- drivers/infiniband/core/cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/infiniband/core/cache.c +2 -1
@@ -1537,7 +1537,8 @@ static void ib_cache_event_task(struct work_struct *_work)* the cache. */ ret =ib_cache_update (work->event.device, work->event.element.port_num, - work->event.event ==IB_EVENT_GID_CHANGE , + work->event.event ==IB_EVENT_GID_CHANGE || + work->event.event ==IB_EVENT_CLIENT_REREGISTER , work->event.event ==IB_EVENT_PKEY_CHANGE , work->enforce_security);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 441/781 RDMA/hns: Fix WQ_MEM_RECLAIM warning
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengchang Tang <[email protected]>
[ Upstream commit c0a26bbd3f99b7b03f072e3409aff4e6ec8af6f6 ]
When sunrpc is used, if a reset triggered, our wq may lead the following trace:
workqueue: WQ_MEM_RECLAIM xprtiod:xprt_rdma_connect_worker [rpcrdma] is flushing !WQ_MEM_RECLAIM hns_roce_irq_workq:flush_work_handle [hns_roce_hw_v2]
WARNING: CPU: 0 PID: 8250 at kernel/workqueue.c:2644 check_flush_dependency+0xe0/0x144 Call trace: check_flush_dependency+0xe0/0x144 start_flush_work.constprop.0+0x1d0/0x2f0 __flush_work.isra.0+0x40/0xb0 flush_work+0x14/0x30 hns_roce_v2_destroy_qp+0xac/0x1e0 [hns_roce_hw_v2] ib_destroy_qp_user+0x9c/0x2b4 rdma_destroy_qp+0x34/0xb0 rpcrdma_ep_destroy+0x28/0xcc [rpcrdma] rpcrdma_ep_put+0x74/0xb4 [rpcrdma] rpcrdma_xprt_disconnect+0x1d8/0x260 [rpcrdma] xprt_rdma_connect_worker+0xc0/0x120 [rpcrdma] process_one_work+0x1cc/0x4d0 worker_thread+0x154/0x414 kthread+0x104/0x144 ret_from_fork+0x10/0x18
Since QP destruction frees memory, this wq should have the WQ_MEM_RECLAIM.
--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +2 -1
@@ -6956,7 +6956,8 @@ static int hns_roce_v2_init_eq_table(struct hns_roce_dev *hr_dev)INIT_WORK (&hr_dev->ecc_work, fmea_ram_ecc_work); - hr_dev->irq_workq =alloc_ordered_workqueue ("hns_roce_irq_workq" ,0 ); + hr_dev->irq_workq =alloc_ordered_workqueue ("hns_roce_irq_workq" , +WQ_MEM_RECLAIM );if (!hr_dev->irq_workq) {dev_err (dev,"failed to create irq workqueue.\n" ); ret = -ENOMEM ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 442/781 RDMA/hns: Return actual error code instead of fixed EINVAL
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junxian Huang <[email protected]>
[ Upstream commit 8cda8acbb1f8c6c0fec45b7166bb558b5af59da8 ]
query_cqc() and query_mpt() may return various error codes in different cases. Return actual error code instead of fixed EINVAL.
--- drivers/infiniband/hw/hns/hns_roce_restrack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/infiniband/hw/hns/hns_roce_restrack.c +2 -2
@@ -51,7 +51,7 @@ int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq) ret = hr_dev->hw->query_cqc (hr_dev, hr_cq->cqn, &context);if (ret) -return -EINVAL ; +return ret; ret =nla_put (msg,RDMA_NLDEV_ATTR_RES_RAW ,sizeof (context), &context);
@@ -177,7 +177,7 @@ int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr) ret = hr_dev->hw->query_mpt (hr_dev, hr_mr->key, &context);if (ret) -return -EINVAL ; +return ret; ret =nla_put (msg,RDMA_NLDEV_ATTR_RES_RAW ,sizeof (context), &context);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 443/781 RDMA/hns: Fix RoCEv1 failure due to DSCP
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junxian Huang <[email protected]>
[ Upstream commit 84bd5d60f0a2b9c763c5e6d0b3d8f4f61f6c5470 ]
DSCP is not supported in RoCEv1, but get_dscp() is still called. If get_dscp() returns an error, it'll eventually cause create_ah to fail even when using RoCEv1.
Correct the return value and avoid calling get_dscp() when using RoCEv1.
--- drivers/infiniband/hw/hns/hns_roce_ah.c | 23 +++++++++--------- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 28 ++++++++++++---------- 2 files changed, 26 insertions(+), 25 deletions(-)
drivers/infiniband/hw/hns/hns_roce_ah.c +11 -12
@@ -60,7 +60,7 @@ int hns_roce_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,u8 tclass =get_tclass (grh);u8 priority =0 ;u8 tc_mode =0 ; -int ret; +int ret =0 ;if (hr_dev->pci_dev->revision ==PCI_REVISION_ID_HIP08 && udata) { ret = -EOPNOTSUPP ;
@@ -77,19 +77,18 @@ int hns_roce_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr, ah->av.flowlabel = grh->flow_label; ah->av.udp_sport =get_ah_udp_sport (ah_attr); ah->av.tclass = tclass; + ah->av.sl =rdma_ah_get_sl (ah_attr); - ret = hr_dev->hw->get_dscp (hr_dev, tclass, &tc_mode, &priority); -if (ret == -EOPNOTSUPP ) - ret =0 ; - -if (ret && grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - goto err_out; +if (grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { + ret = hr_dev->hw->get_dscp (hr_dev, tclass, &tc_mode, &priority); +if (ret == -EOPNOTSUPP ) + ret =0 ; +else if (ret) + goto err_out; -if (tc_mode ==HNAE3_TC_MAP_MODE_DSCP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - ah->av.sl = priority; -else - ah->av.sl =rdma_ah_get_sl (ah_attr); +if (tc_mode ==HNAE3_TC_MAP_MODE_DSCP ) + ah->av.sl = priority; + }if (!check_sl_valid (hr_dev, ah->av.sl)) { ret = -EINVAL ;
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +15 -13
@@ -5053,20 +5053,22 @@ static int hns_roce_set_sl(struct ib_qp *ibqp,struct ib_device *ibdev = &hr_dev->ib_dev;int ret; - ret =hns_roce_hw_v2_get_dscp (hr_dev,get_tclass (&attr->ah_attr.grh), - &hr_qp->tc_mode, &hr_qp->priority); -if (ret && ret != -EOPNOTSUPP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { -ibdev_err_ratelimited (ibdev, -"failed to get dscp, ret = %d.\n" , ret); -return ret; - } + hr_qp->sl =rdma_ah_get_sl (&attr->ah_attr); -if (hr_qp->tc_mode ==HNAE3_TC_MAP_MODE_DSCP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - hr_qp->sl = hr_qp->priority; -else - hr_qp->sl =rdma_ah_get_sl (&attr->ah_attr); +if (grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { + ret =hns_roce_hw_v2_get_dscp (hr_dev, +get_tclass (&attr->ah_attr.grh), + &hr_qp->tc_mode, &hr_qp->priority); +if (ret && ret != -EOPNOTSUPP ) { +ibdev_err_ratelimited (ibdev, +"failed to get dscp, ret = %d.\n" , + ret); +return ret; + } + +if (hr_qp->tc_mode ==HNAE3_TC_MAP_MODE_DSCP ) + hr_qp->sl = hr_qp->priority; + }if (!check_sl_valid (hr_dev, hr_qp->sl))return -EINVAL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 444/781 RDMA/hns: Notify ULP of remaining soft-WCs during reset
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengchang Tang <[email protected]>
[ Upstream commit 0789f929900d85b80b343c5f04f8b9444e991384 ]
During a reset, software-generated WCs cannot be reported via interrupts. This may cause the ULP to miss some WCs.
To avoid this, add check in the CQ arm process: if a hardware reset has occurred and there are still unreported soft-WCs, notify the ULP to handle the remaining WCs, thereby preventing any loss of completions.
--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +23 -0
@@ -3739,6 +3739,23 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev, HNS_ROCE_V2_CQ_DEFAULT_INTERVAL); } +static bool left_sw_wc (struct hns_roce_dev *hr_dev,struct hns_roce_cq *hr_cq) +{ +struct hns_roce_qp *hr_qp; + +list_for_each_entry (hr_qp, &hr_cq->sq_list, sq_node) { +if (hr_qp->sq.head != hr_qp->sq.tail) +return true; + } + +list_for_each_entry (hr_qp, &hr_cq->rq_list, rq_node) { +if (hr_qp->rq.head != hr_qp->rq.tail) +return true; + } + +return false; +} +static int hns_roce_v2_req_notify_cq (struct ib_cq *ibcq,enum ib_cq_notify_flags flags) {
@@ -3747,6 +3764,12 @@ static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq,struct hns_roce_v2_db cq_db = {};u32 notify_flag; +if (hr_dev->state >=HNS_ROCE_DEVICE_STATE_RST_DOWN ) { +if ((flags &IB_CQ_REPORT_MISSED_EVENTS ) && +left_sw_wc (hr_dev, hr_cq)) +return 1 ; +return 0 ; + }/* * flags = 0, then notify_flag : next * flags = 1, then notify flag : solocited
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 445/781 RDMA/mlx5: Fix ucaps init error flow
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maher Sanalla <[email protected]>
[ Upstream commit 6dc78c53de99e4ed9868d4f0fc6da6e46f52fe4d ]
In mlx5_ib_stage_caps_init(), if mlx5_ib_init_ucaps() fails after mlx5_ib_init_var_table() succeeds, the VAR bitmap is leaked since the function returns without cleanup.
Thus, cleanup the var table bitmap in case of error of initializing ucaps before exiting, preventing the leak above.
--- drivers/infiniband/hw/mlx5/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/infiniband/hw/mlx5/main.c +5 -1
@@ -4466,12 +4466,16 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) MLX5_HCA_CAP_2_GENERAL_OBJECT_TYPES_RDMA_CTRL) { err =mlx5_ib_init_ucaps (dev);if (err) -return err; + goto err_ucaps; } dev->ib_dev.use_cq_dim = true;return 0 ; + +err_ucaps: +bitmap_free (dev->var_table.bitmap); +return err; } static const struct ib_device_ops mlx5_ib_dev_port_ops = {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 446/781 cxl/mem: Fix devm_cxl_memdev_edac_release() confusion
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Williams <[email protected]>
[ Upstream commit 10016118b6fade907143a32a7aeaa777063dc79c ]
A device release method is only for undoing allocations on the path to preparing the device for device_add(). In contrast, devm allocations are post device_add(), are acquired during / after ->probe() and are released synchronous with ->remove().
So, a "devm" helper in a "release" method is a clear anti-pattern.
Move this devm release action where it belongs, an action created at edac object creation time. Otherwise, this leaks resources until cxl_memdev_release() time which may be long after these xarray and error record caches have gone idle.
Note, this also fixes up the type of @cxlmd->err_rec_array which needlessly dropped type-safety.
--- drivers/cxl/core/edac.c | 64 ++++++++++++++++++++++----------------- drivers/cxl/core/memdev.c | 1 - drivers/cxl/cxlmem.h | 5 +-- 3 files changed, 38 insertions(+), 32 deletions(-)
drivers/cxl/core/edac.c +37 -27
@@ -1988,6 +1988,40 @@ static int cxl_memdev_soft_ppr_init(struct cxl_memdev *cxlmd,return 0 ; } +static void err_rec_free (void *_cxlmd) +{ +struct cxl_memdev *cxlmd = _cxlmd; +struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; +struct cxl_event_gen_media *rec_gen_media; +struct cxl_event_dram *rec_dram; +unsigned long index; + + cxlmd->err_rec_array =NULL ; +xa_for_each (&array_rec->rec_dram, index, rec_dram) +kfree (rec_dram); +xa_destroy (&array_rec->rec_dram); + +xa_for_each (&array_rec->rec_gen_media, index, rec_gen_media) +kfree (rec_gen_media); +xa_destroy (&array_rec->rec_gen_media); +kfree (array_rec); +} + +static int devm_cxl_memdev_setup_err_rec (struct cxl_memdev *cxlmd) +{ +struct cxl_mem_err_rec *array_rec = +kzalloc (sizeof (*array_rec),GFP_KERNEL ); + +if (!array_rec) +return -ENOMEM ; + +xa_init (&array_rec->rec_gen_media); +xa_init (&array_rec->rec_dram); + cxlmd->err_rec_array = array_rec; + +return devm_add_action_or_reset (&cxlmd->dev, err_rec_free, cxlmd); +} +int devm_cxl_memdev_edac_register (struct cxl_memdev *cxlmd) {struct edac_dev_feature ras_features[CXL_NR_EDAC_DEV_FEATURES ];
@@ -2038,15 +2072,9 @@ int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd) }if (repair_inst) { -struct cxl_mem_err_rec *array_rec = -devm_kzalloc (&cxlmd->dev,sizeof (*array_rec), -GFP_KERNEL ); -if (!array_rec) -return -ENOMEM ; - -xa_init (&array_rec->rec_gen_media); -xa_init (&array_rec->rec_dram); - cxlmd->err_rec_array = array_rec; + rc =devm_cxl_memdev_setup_err_rec (cxlmd); +if (rc) +return rc; } }
@@ -2088,22 +2116,4 @@ int devm_cxl_region_edac_register(struct cxl_region *cxlr) }EXPORT_SYMBOL_NS_GPL (devm_cxl_region_edac_register,"CXL" ); -void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd) -{ -struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; -struct cxl_event_gen_media *rec_gen_media; -struct cxl_event_dram *rec_dram; -unsigned long index; - -if (!IS_ENABLED (CONFIG_CXL_EDAC_MEM_REPAIR ) || !array_rec) -return ; - -xa_for_each (&array_rec->rec_dram, index, rec_dram) -kfree (rec_dram); -xa_destroy (&array_rec->rec_dram); -xa_for_each (&array_rec->rec_gen_media, index, rec_gen_media) -kfree (rec_gen_media); -xa_destroy (&array_rec->rec_gen_media); -} -EXPORT_SYMBOL_NS_GPL (devm_cxl_memdev_edac_release,"CXL" );
drivers/cxl/core/memdev.c +0 -1
@@ -27,7 +27,6 @@ static void cxl_memdev_release(struct device *dev)struct cxl_memdev *cxlmd =to_cxl_memdev (dev);ida_free (&cxl_memdev_ida, cxlmd->id); -devm_cxl_memdev_edac_release (cxlmd);kfree (cxlmd); }
drivers/cxl/cxlmem.h +1 -4
@@ -63,7 +63,7 @@ struct cxl_memdev {int depth;u8 scrub_cycle;int scrub_region_id; -void *err_rec_array; +struct cxl_mem_err_rec *err_rec_array; };static inline struct cxl_memdev *to_cxl_memdev (struct device *dev)
@@ -877,7 +877,6 @@ int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd);int devm_cxl_region_edac_register (struct cxl_region *cxlr);int cxl_store_rec_gen_media (struct cxl_memdev *cxlmd,union cxl_event *evt);int cxl_store_rec_dram (struct cxl_memdev *cxlmd,union cxl_event *evt); -void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd);#else static inline int devm_cxl_memdev_edac_register (struct cxl_memdev *cxlmd) {return 0 ; }
@@ -889,8 +888,6 @@ static inline int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd,static inline int cxl_store_rec_dram (struct cxl_memdev *cxlmd,union cxl_event *evt) {return 0 ; } -static inline void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd) -{return ; }#endif #ifdef CONFIG_CXL_SUSPEND
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 447/781 power: supply: ab8500: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit c4af8a98bb52825a5331ae1d0604c0ea6956ba4b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Commit 1c1f13a006ed ("power: supply: ab8500: Move to componentized binding") introduced this issue during a refactorization. Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/ab8500_charger.c | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-)
drivers/power/supply/ab8500_charger.c +20 -20
@@ -3466,26 +3466,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)return ret; } -/* Request interrupts */ -for (i =0 ; i <ARRAY_SIZE (ab8500_charger_irq); i++) { - irq =platform_get_irq_byname (pdev, ab8500_charger_irq[i].name); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, - irq,NULL , ab8500_charger_irq[i].isr, -IRQF_SHARED |IRQF_NO_SUSPEND |IRQF_ONESHOT , - ab8500_charger_irq[i].name, di); - -if (ret !=0 ) { -dev_err (dev,"failed to request %s IRQ %d: %d\n" - , ab8500_charger_irq[i].name, irq, ret); -return ret; - } -dev_dbg (dev,"Requested %s IRQ %d: %d\n" , - ab8500_charger_irq[i].name, irq, ret); - } -/* initialize lock */ spin_lock_init (&di->usb_state.usb_lock);mutex_init (&di->usb_ipt_crnt_lock);
@@ -3614,6 +3594,26 @@ static int ab8500_charger_probe(struct platform_device *pdev)return PTR_ERR (di->usb_chg.psy); } +/* Request interrupts */ +for (i =0 ; i <ARRAY_SIZE (ab8500_charger_irq); i++) { + irq =platform_get_irq_byname (pdev, ab8500_charger_irq[i].name); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, + irq,NULL , ab8500_charger_irq[i].isr, +IRQF_SHARED |IRQF_NO_SUSPEND |IRQF_ONESHOT , + ab8500_charger_irq[i].name, di); + +if (ret !=0 ) { +dev_err (dev,"failed to request %s IRQ %d: %d\n" + , ab8500_charger_irq[i].name, irq, ret); +return ret; + } +dev_dbg (dev,"Requested %s IRQ %d: %d\n" , + ab8500_charger_irq[i].name, irq, ret); + } +/* * Check what battery we have, since we always have the USB * psy, use that as a handle.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 448/781 power: supply: act8945a: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 3291c51d4684d048dd2eb91b5b65fcfdaf72141f ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/act8945a_charger.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/power/supply/act8945a_charger.c +8 -8
@@ -597,14 +597,6 @@ static int act8945a_charger_probe(struct platform_device *pdev)return irq ?: -ENXIO ; } - ret =devm_request_irq (&pdev->dev, irq, act8945a_status_changed, -IRQF_TRIGGER_FALLING ,"act8945a_interrupt" , - charger); -if (ret) { -dev_err (&pdev->dev,"failed to request nIRQ pin IRQ\n" ); -return ret; - } - charger->desc.name ="act8945a-charger" ; charger->desc.get_property = act8945a_charger_get_property; charger->desc.properties = act8945a_charger_props;
@@ -625,6 +617,14 @@ static int act8945a_charger_probe(struct platform_device *pdev)return PTR_ERR (charger->psy); } + ret =devm_request_irq (&pdev->dev, irq, act8945a_status_changed, +IRQF_TRIGGER_FALLING ,"act8945a_interrupt" , + charger); +if (ret) { +dev_err (&pdev->dev,"failed to request nIRQ pin IRQ\n" ); +return ret; + } +platform_set_drvdata (pdev, charger);INIT_WORK (&charger->work, act8945a_work);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 449/781 power: supply: bq256xx: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 8005843369723d9c8975b7c4202d1b85d6125302 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/bq256xx_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/bq256xx_charger.c +6 -6
@@ -1741,6 +1741,12 @@ static int bq256xx_probe(struct i2c_client *client)usb_register_notifier (bq->usb3_phy, &bq->usb_nb); } + ret =bq256xx_power_supply_init (bq, &psy_cfg, dev); +if (ret) { +dev_err (dev,"Failed to register power supply\n" ); +return ret; + } +if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq256xx_irq_handler_thread,
@@ -1753,12 +1759,6 @@ static int bq256xx_probe(struct i2c_client *client) } } - ret =bq256xx_power_supply_init (bq, &psy_cfg, dev); -if (ret) { -dev_err (dev,"Failed to register power supply\n" ); -return ret; - } - ret =bq256xx_hw_init (bq);if (ret) {dev_err (dev,"Cannot initialize the chip.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 450/781 power: supply: bq25980: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 5f0b1cb41906e86b64bf69f5ededb83b0d757c27 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/bq25980_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/bq25980_charger.c +6 -6
@@ -1241,6 +1241,12 @@ static int bq25980_probe(struct i2c_client *client)return ret; } + ret =bq25980_power_supply_init (bq, dev); +if (ret) { +dev_err (dev,"Failed to register power supply\n" ); +return ret; + } +if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq25980_irq_handler_thread,
@@ -1251,12 +1257,6 @@ static int bq25980_probe(struct i2c_client *client)return ret; } - ret =bq25980_power_supply_init (bq, dev); -if (ret) { -dev_err (dev,"Failed to register power supply\n" ); -return ret; - } - ret =bq25980_hw_init (bq);if (ret) {dev_err (dev,"Cannot initialize the chip.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 451/781 power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 642f33e34b969eedec334738fd5df95d2dc42742 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/cpcap-battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/power/supply/cpcap-battery.c +4 -4
@@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev)platform_set_drvdata (pdev, ddata); - error =cpcap_battery_init_interrupts (pdev, ddata); -if (error) -return error; - error =cpcap_battery_init_iio (ddata);if (error)return error;
@@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev)return error; } + error =cpcap_battery_init_interrupts (pdev, ddata); +if (error) +return error; +atomic_set (&ddata->active,1 ); error =cpcap_battery_calibrate (ddata);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 452/781 power: supply: goldfish: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit b2ce982e2e0c888dc55c888ad0e20ea04daf2e6b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/goldfish_battery.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/goldfish_battery.c +6 -6
@@ -224,12 +224,6 @@ static int goldfish_battery_probe(struct platform_device *pdev)if (data->irq <0 )return -ENODEV ; - ret =devm_request_irq (&pdev->dev, data->irq, - goldfish_battery_interrupt, -IRQF_SHARED , pdev->name, data); -if (ret) -return ret; - psy_cfg.drv_data = data; data->ac = devm_power_supply_register(&pdev->dev,
@@ -244,6 +238,12 @@ static int goldfish_battery_probe(struct platform_device *pdev)if (IS_ERR (data->battery))return PTR_ERR (data->battery); + ret =devm_request_irq (&pdev->dev, data->irq, + goldfish_battery_interrupt, +IRQF_SHARED , pdev->name, data); +if (ret) +return ret; +GOLDFISH_BATTERY_WRITE (data,BATTERY_INT_ENABLE ,BATTERY_INT_MASK );return 0 ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 453/781 power: supply: pf1550: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 838767f5074700552d3f006d867caed65edc7328 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/pf1550-charger.c | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/power/supply/pf1550-charger.c +16 -16
@@ -584,22 +584,6 @@ static int pf1550_charger_probe(struct platform_device *pdev)return dev_err_probe (chg->dev, ret,"failed to add battery sense work\n" ); -for (i =0 ; i <PF1550_CHARGER_IRQ_NR ; i++) { - irq =platform_get_irq (pdev, i); -if (irq <0 ) -return irq; - - chg->virqs[i] = irq; - - ret =devm_request_threaded_irq (&pdev->dev, irq,NULL , - pf1550_charger_irq_handler, -IRQF_NO_SUSPEND , -"pf1550-charger" , chg); -if (ret) -return dev_err_probe (&pdev->dev, ret, -"failed irq request\n" ); - } - psy_cfg.drv_data = chg; chg->charger = devm_power_supply_register(&pdev->dev,
@@ -616,6 +600,22 @@ static int pf1550_charger_probe(struct platform_device *pdev)return dev_err_probe (&pdev->dev,PTR_ERR (chg->battery),"failed: power supply register\n" ); +for (i =0 ; i <PF1550_CHARGER_IRQ_NR ; i++) { + irq =platform_get_irq (pdev, i); +if (irq <0 ) +return irq; + + chg->virqs[i] = irq; + + ret =devm_request_threaded_irq (&pdev->dev, irq,NULL , + pf1550_charger_irq_handler, +IRQF_NO_SUSPEND , +"pf1550-charger" , chg); +if (ret) +return dev_err_probe (&pdev->dev, ret, +"failed irq request\n" ); + } +pf1550_dt_parse_dev_info (chg);return pf1550_reg_init (chg);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 454/781 power: supply: pm8916_bms_vm: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 62914959b35e9a1e29cc0f64cb8cfc5075a5366f ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/pm8916_bms_vm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/power/supply/pm8916_bms_vm.c +9 -9
@@ -167,15 +167,6 @@ static int pm8916_bms_vm_battery_probe(struct platform_device *pdev)if (ret <0 )return -EINVAL ; - irq =platform_get_irq_byname (pdev,"fifo" ); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_bms_vm_fifo_update_done_irq, -IRQF_ONESHOT ,"pm8916_vm_bms" , bat); -if (ret) -return ret; - ret =regmap_bulk_read (bat->regmap, bat->reg +PM8916_PERPH_TYPE , &tmp,2 );if (ret) goto comm_error;
@@ -220,6 +211,15 @@ static int pm8916_bms_vm_battery_probe(struct platform_device *pdev)if (ret)return dev_err_probe (dev, ret,"Unable to get battery info\n" ); + irq =platform_get_irq_byname (pdev,"fifo" ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_bms_vm_fifo_update_done_irq, +IRQF_ONESHOT ,"pm8916_vm_bms" , bat); +if (ret) +return ret; +platform_set_drvdata (pdev, bat);return 0 ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 455/781 power: supply: pm8916_lbc: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit b7508129978ae1e2ed9b0410396abc05def9c4eb ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/pm8916_lbc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/power/supply/pm8916_lbc.c +9 -9
@@ -274,15 +274,6 @@ static int pm8916_lbc_charger_probe(struct platform_device *pdev)return dev_err_probe (dev, -EINVAL ,"Wrong amount of reg values: %d (4 expected)\n" , len); - irq =platform_get_irq_byname (pdev,"usb_vbus" ); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_lbc_charger_state_changed_irq, -IRQF_ONESHOT ,"pm8916_lbc" , chg); -if (ret) -return ret; - ret =device_property_read_u32_array (dev,"reg" , chg->reg, len);if (ret)return ret;
@@ -332,6 +323,15 @@ static int pm8916_lbc_charger_probe(struct platform_device *pdev)if (ret)return dev_err_probe (dev, ret,"Unable to get battery info\n" ); + irq =platform_get_irq_byname (pdev,"usb_vbus" ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_lbc_charger_state_changed_irq, +IRQF_ONESHOT ,"pm8916_lbc" , chg); +if (ret) +return ret; + chg->edev =devm_extcon_dev_allocate (dev, pm8916_lbc_charger_cable);if (IS_ERR (chg->edev))return PTR_ERR (chg->edev);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 456/781 power: supply: rt9455: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit e2febe375e5ea5afed92f4cd9711bde8f24ee6d2 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/rt9455_charger.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/power/supply/rt9455_charger.c +9 -8
@@ -1663,6 +1663,15 @@ static int rt9455_probe(struct i2c_client *client) rt9455_charger_config.supplied_to = rt9455_charger_supplied_to; rt9455_charger_config.num_supplicants =ARRAY_SIZE (rt9455_charger_supplied_to); + + info->charger =devm_power_supply_register (dev, &rt9455_charger_desc, + &rt9455_charger_config); +if (IS_ERR (info->charger)) { +dev_err (dev,"Failed to register charger\n" ); + ret =PTR_ERR (info->charger); + goto put_usb_notifier; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, rt9455_irq_handler_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
@@ -1678,14 +1687,6 @@ static int rt9455_probe(struct i2c_client *client) goto put_usb_notifier; } - info->charger =devm_power_supply_register (dev, &rt9455_charger_desc, - &rt9455_charger_config); -if (IS_ERR (info->charger)) { -dev_err (dev,"Failed to register charger\n" ); - ret =PTR_ERR (info->charger); - goto put_usb_notifier; - } -return 0 ; put_usb_notifier:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 457/781 power: supply: sbs-battery: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 8d59cf3887fbabacef53bfba473e33e8a8d9d07b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle. Keep the old behavior of just printing a warning in case of any failures during the IRQ request and finishing the probe successfully.
--- drivers/power/supply/sbs-battery.c | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-)
drivers/power/supply/sbs-battery.c +18 -18
@@ -1174,24 +1174,6 @@ static int sbs_probe(struct i2c_client *client)i2c_set_clientdata (client, chip); -if (!chip->gpio_detect) - goto skip_gpio; - - irq =gpiod_to_irq (chip->gpio_detect); -if (irq <=0 ) { -dev_warn (&client->dev,"Failed to get gpio as irq: %d\n" , irq); - goto skip_gpio; - } - - rc =devm_request_threaded_irq (&client->dev, irq,NULL , sbs_irq, -IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT , -dev_name (&client->dev), chip); -if (rc) { -dev_warn (&client->dev,"Failed to request irq: %d\n" , rc); - goto skip_gpio; - } - -skip_gpio:/* * Before we register, we might need to make sure we can actually talk * to the battery.
@@ -1217,6 +1199,24 @@ static int sbs_probe(struct i2c_client *client)return dev_err_probe (&client->dev,PTR_ERR (chip->power_supply),"Failed to register power supply\n" ); +if (!chip->gpio_detect) + goto out; + + irq =gpiod_to_irq (chip->gpio_detect); +if (irq <=0 ) { +dev_warn (&client->dev,"Failed to get gpio as irq: %d\n" , irq); + goto out; + } + + rc =devm_request_threaded_irq (&client->dev, irq,NULL , sbs_irq, +IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT , +dev_name (&client->dev), chip); +if (rc) { +dev_warn (&client->dev,"Failed to request irq: %d\n" , rc); + goto out; + } + +out:dev_info (&client->dev,"%s: battery gas gauge device registered\n" , client->name);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 458/781 power: reset: nvmem-reboot-mode: respect cell size for nvmem_cell_write
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Koskovich <[email protected]>
[ Upstream commit 36b05629226413836cfbb3fbe6689cd188bca156 ]
Some platforms expose reboot mode cells that are smaller than an unsigned int, in which cases lead to write failures. Read the cell first to determine actual size and only write the number of bytes the cell can hold.
--- drivers/power/reset/nvmem-reboot-mode.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/power/reset/nvmem-reboot-mode.c +13 -2
@@ -10,6 +10,7 @@#include <linux/nvmem-consumer.h> #include <linux/platform_device.h> #include <linux/reboot-mode.h> +#include <linux/slab.h> struct nvmem_reboot_mode {struct reboot_mode_driver reboot;
@@ -19,12 +20,22 @@ struct nvmem_reboot_mode {static int nvmem_reboot_mode_write (struct reboot_mode_driver *reboot,unsigned int magic) { -int ret;struct nvmem_reboot_mode *nvmem_rbm; +size_t buf_len; +void *buf; +int ret; nvmem_rbm = container_of(reboot, struct nvmem_reboot_mode, reboot); - ret =nvmem_cell_write (nvmem_rbm->cell, &magic,sizeof (magic)); + buf =nvmem_cell_read (nvmem_rbm->cell, &buf_len); +if (IS_ERR (buf)) +return PTR_ERR (buf); +kfree (buf); + +if (buf_len >sizeof (magic)) +return -EINVAL ; + + ret =nvmem_cell_write (nvmem_rbm->cell, &magic, buf_len);if (ret <0 )dev_err (reboot->dev,"update reboot mode bits failed\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 459/781 power: supply: bq27xxx: fix wrong errno when bus ops are unsupported
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haotian Zhang <[email protected]>
[ Upstream commit 688364a11647dc09ba1e4429313e0008066ec790 ]
bq27xxx_write(), bq27xxx_read_block(), and bq27xxx_write_block() return -EPERM when the bus callback pointer is NULL. A NULL callback indicates the operation is not supported by the bus/driver, not that permission is denied.
Return -EOPNOTSUPP instead of -EPERM when di->bus.write/ read_bulk/write_bulk is NULL.
--- drivers/power/supply/bq27xxx_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/power/supply/bq27xxx_battery.c +3 -3
@@ -1172,7 +1172,7 @@ static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index,return -EINVAL ;if (!di->bus.write) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.write (di, di->regs[reg_index], value, single);if (ret <0 )
@@ -1191,7 +1191,7 @@ static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_indreturn -EINVAL ;if (!di->bus.read_bulk) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.read_bulk (di, di->regs[reg_index], data, len);if (ret <0 )
@@ -1210,7 +1210,7 @@ static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_inreturn -EINVAL ;if (!di->bus.write_bulk) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.write_bulk (di, di->regs[reg_index], data, len);if (ret <0 )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 460/781 power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 39fe0eac6d755ef215026518985fcf8de9360e9e ]
In probe(), request_irq() is called before allocating/registering a power_supply handle. If an interrupt is fired between the call to request_irq() and power_supply_register(), the power_supply handle will be used uninitialized in power_supply_changed() in wm97xx_bat_update() (triggered from the interrupt handler). This will lead to a NULL pointer dereference since
Fix this racy NULL pointer dereference by making sure the IRQ is requested _after_ the registration of the power_supply handle. Since the IRQ is the last thing requests in the probe() now, remove the error path for freeing it. Instead add one for unregistering the power_supply handle when IRQ request fails.
--- drivers/power/supply/wm97xx_battery.c | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-)
drivers/power/supply/wm97xx_battery.c +19 -15
@@ -178,12 +178,6 @@ static int wm97xx_bat_probe(struct platform_device *dev) "failed to get charge GPIO\n");if (charge_gpiod) {gpiod_set_consumer_name (charge_gpiod,"BATT CHRG" ); - ret =request_irq (gpiod_to_irq (charge_gpiod), - wm97xx_chrg_irq,0 , -"AC Detect" , dev); -if (ret) -return dev_err_probe (&dev->dev, ret, -"failed to request GPIO irq\n" ); props++;/* POWER_SUPPLY_PROP_STATUS */ }
@@ -199,10 +193,8 @@ static int wm97xx_bat_probe(struct platform_device *dev) props++;/* POWER_SUPPLY_PROP_VOLTAGE_MIN */ prop =kcalloc (props,sizeof (*prop),GFP_KERNEL ); -if (!prop) { - ret = -ENOMEM ; - goto err3; - } +if (!prop) +return -ENOMEM ; prop[i++] =POWER_SUPPLY_PROP_PRESENT ;if (charge_gpiod)
@@ -236,15 +228,27 @@ static int wm97xx_bat_probe(struct platform_device *dev)schedule_work (&bat_work); }else { ret =PTR_ERR (bat_psy); - goto err4; + goto free; + } + +if (charge_gpiod) { + ret =request_irq (gpiod_to_irq (charge_gpiod), wm97xx_chrg_irq, +0 ,"AC Detect" , dev); +if (ret) { +dev_err_probe (&dev->dev, ret, +"failed to request GPIO irq\n" ); + goto unregister; + } }return 0 ; -err4: + +unregister: +power_supply_unregister (bat_psy); + +free:kfree (prop); -err3: -if (charge_gpiod) -free_irq (gpiod_to_irq (charge_gpiod), dev); +return ret; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 461/781 RDMA/rtrs-srv: fix SG mapping
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roman Penyaev <[email protected]>
[ Upstream commit 83835f7c07b523c7ca2a5ad0a511670b5810539e ]
This fixes the following error on the server side:
RTRS server session allocation failed: -EINVAL
caused by the caller of the ib_dma_map_sg(), which does not expect less mapped entries, than requested, which is in the order of things and can be easily reproduced on the machine with enabled IOMMU.
The fix is to treat any positive number of mapped sg entries as a successful mapping and cache DMA addresses by traversing modified SG table.
--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/infiniband/ulp/rtrs/rtrs-srv.c +20 -5
@@ -595,7 +595,7 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) srv_path->mrs_num++) {struct rtrs_srv_mr *srv_mr = &srv_path->mrs[srv_path->mrs_num];struct scatterlist *s; -int nr, nr_sgt, chunks; +int nr, nr_sgt, chunks, ind; sgt = &srv_mr->sgt; chunks = chunks_per_mr * srv_path->mrs_num;
@@ -625,7 +625,7 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) } nr =ib_map_mr_sg (mr, sgt->sgl, nr_sgt,NULL , max_chunk_size); -if (nr != nr_sgt) { +if (nr < nr_sgt) { err = nr <0 ? nr : -EINVAL ; goto dereg_mr; }
@@ -641,9 +641,24 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) goto dereg_mr; } } -/* Eventually dma addr for each chunk can be cached */ -for_each_sg (sgt->sgl, s, nr_sgt, i) - srv_path->dma_addr[chunks + i] =sg_dma_address (s); + +/* +* Cache DMA addresses by traversing sg entries. If +* regions were merged, an inner loop is required to +* populate the DMA address array by traversing larger +* regions. +*/ + ind = chunks; +for_each_sg (sgt->sgl, s, nr_sgt, i) { +unsigned int dma_len =sg_dma_len (s); +u64 dma_addr =sg_dma_address (s); +u64 dma_addr_end = dma_addr + dma_len; + +do { + srv_path->dma_addr[ind++] = dma_addr; + dma_addr += max_chunk_size; + }while (dma_addr < dma_addr_end); + }ib_update_fast_reg_key (mr,ib_inc_rkey (mr->rkey)); srv_mr->mr = mr;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 462/781 RDMA/rxe: Fix double free in rxe_srq_from_init
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiasheng Jiang <[email protected]>
[ Upstream commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 ]
In rxe_srq_from_init(), the queue pointer 'q' is assigned to 'srq->rq.queue' before copying the SRQ number to user space. If copy_to_user() fails, the function calls rxe_queue_cleanup() to free the queue, but leaves the now-invalid pointer in 'srq->rq.queue'.
The caller of rxe_srq_from_init() (rxe_create_srq) eventually calls rxe_srq_cleanup() upon receiving the error, which triggers a second rxe_queue_cleanup() on the same memory, leading to a double free.
The call trace looks like this:
kmem_cache_free+0x.../0x... rxe_queue_cleanup+0x1a/0x30 [rdma_rxe] rxe_srq_cleanup+0x42/0x60 [rdma_rxe] rxe_elem_release+0x31/0x70 [rdma_rxe] rxe_create_srq+0x12b/0x1a0 [rdma_rxe] ib_create_srq_user+0x9a/0x150 [ib_core]
Fix this by moving 'srq->rq.queue = q' after copy_to_user.
--- drivers/infiniband/sw/rxe/rxe_srq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/infiniband/sw/rxe/rxe_srq.c +3 -3
@@ -77,9 +77,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, goto err_free; } - srq->rq.queue = q; - init->attr.max_wr = srq->rq.max_wr; -if (uresp) {if (copy_to_user (&uresp->srq_num, &srq->srq_num,sizeof (uresp->srq_num))) {
@@ -88,6 +85,9 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, } } + srq->rq.queue = q; + init->attr.max_wr = srq->rq.max_wr; +return 0 ; err_free:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 463/781 RDMA/iwcm: Fix workqueue list corruption by removing work_list
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Moroni <[email protected]>
[ Upstream commit 7874eeacfa42177565c01d5198726671acf7adf2 ]
The commit e1168f0 ("RDMA/iwcm: Simplify cm_event_handler()") changed the work submission logic to unconditionally call queue_work() with the expectation that queue_work() would have no effect if work was already pending. The problem is that a free list of struct iwcm_work is used (for which struct work_struct is embedded), so each call to queue_work() is basically unique and therefore does indeed queue the work.
This causes a problem in the work handler which walks the work_list until it's empty to process entries. This means that a single run of the work handler could process item N+1 and release it back to the free list while the actual workqueue entry is still queued. It could then get reused (INIT_WORK...) and lead to list corruption in the workqueue logic.
Fix this by just removing the work_list. The workqueue already does this for us.
This fixes the following error that was observed when stress testing with ucmatose on an Intel E830 in iWARP mode:
[ 151.465780] list_del corruption. next->prev should be ffff9f0915c69c08, but was ffff9f0a1116be08. (next=ffff9f0a15b11c08) [ 151.466639] ------------[ cut here ]------------ [ 151.466986] kernel BUG at lib/list_debug.c:67! [ 151.467349] Oops: invalid opcode: 0000 [#1] SMP NOPTI [ 151.467753] CPU: 14 UID: 0 PID: 2306 Comm: kworker/u64:18 Not tainted 6.19.0-rc4+ #1 PREEMPT(voluntary) [ 151.468466] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 151.469192] Workqueue: 0x0 (iw_cm_wq) [ 151.469478] RIP: 0010:__list_del_entry_valid_or_report+0xf0/0x100 [ 151.469942] Code: c7 58 5f 4c b2 e8 10 50 aa ff 0f 0b 48 89 ef e8 36 57 cb ff 48 8b 55 08 48 89 e9 48 89 de 48 c7 c7 a8 5f 4c b2 e8 f0 4f aa ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 90 90 90 90 90 90 [ 151.471323] RSP: 0000:ffffb15644e7bd68 EFLAGS: 00010046 [ 151.471712] RAX: 000000000000006d RBX: ffff9f0915c69c08 RCX: 0000000000000027 [ 151.472243] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff9f0a37d9c600 [ 151.472768] RBP: ffff9f0a15b11c08 R08: 0000000000000000 R09: c0000000ffff7fff [ 151.473294] R10: 0000000000000001 R11: ffffb15644e7bba8 R12: ffff9f092339ee68 [ 151.473817] R13: ffff9f0900059c28 R14: ffff9f092339ee78 R15: 0000000000000000 [ 151.474344] FS: 0000000000000000(0000) GS:ffff9f0a847b5000(0000) knlGS:0000000000000000 [ 151.474934] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 151.475362] CR2: 0000559e233a9088 CR3: 000000020296b004 CR4: 0000000000770ef0 [ 151.475895] PKRU: 55555554 [ 151.476118] Call Trace: [ 151.476331] <TASK> [ 151.476497] move_linked_works+0x49/0xa0 [ 151.476792] __pwq_activate_work.isra.46+0x2f/0xa0 [ 151.477151] pwq_dec_nr_in_flight+0x1e0/0x2f0 [ 151.477479] process_scheduled_works+0x1c8/0x410 [ 151.477823] worker_thread+0x125/0x260 [ 151.478108] ? __pfx_worker_thread+0x10/0x10 [ 151.478430] kthread+0xfe/0x240 [ 151.478671] ? __pfx_kthread+0x10/0x10 [ 151.478955] ? __pfx_kthread+0x10/0x10 [ 151.479240] ret_from_fork+0x208/0x270 [ 151.479523] ? __pfx_kthread+0x10/0x10 [ 151.479806] ret_from_fork_asm+0x1a/0x30 [ 151.480103] </TASK>
--- drivers/infiniband/core/iwcm.c | 56 +++++++++++++--------------------- drivers/infiniband/core/iwcm.h | 1 - 2 files changed, 21 insertions(+), 36 deletions(-)
drivers/infiniband/core/iwcm.c +21 -35
@@ -95,7 +95,6 @@ static struct workqueue_struct *iwcm_wq;struct iwcm_work {struct work_struct work;struct iwcm_id_private *cm_id; -struct list_head list;struct iw_cm_event event;struct list_head free_list; };
@@ -178,7 +177,6 @@ static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)return -ENOMEM ; } work->cm_id = cm_id_priv; -INIT_LIST_HEAD (&work->list);put_work (work); }return 0 ;
@@ -213,7 +211,6 @@ static void free_cm_id(struct iwcm_id_private *cm_id_priv)static bool iwcm_deref_id (struct iwcm_id_private *cm_id_priv) {if (refcount_dec_and_test (&cm_id_priv->refcount)) { -BUG_ON (!list_empty (&cm_id_priv->work_list));free_cm_id (cm_id_priv);return true; }
@@ -260,7 +257,6 @@ struct iw_cm_id *iw_create_cm_id(struct ib_device *device,refcount_set (&cm_id_priv->refcount,1 );init_waitqueue_head (&cm_id_priv->connect_wait);init_completion (&cm_id_priv->destroy_comp); -INIT_LIST_HEAD (&cm_id_priv->work_list);INIT_LIST_HEAD (&cm_id_priv->work_free_list);return &cm_id_priv->id;
@@ -1007,13 +1003,13 @@ static int process_event(struct iwcm_id_private *cm_id_priv, }/* -* Process events on the work_list for the cm_id. If the callback -* function requests that the cm_id be deleted, a flag is set in the -* cm_id flags to indicate that when the last reference is -* removed, the cm_id is to be destroyed. This is necessary to -* distinguish between an object that will be destroyed by the app -* thread asleep on the destroy_comp list vs. an object destroyed -* here synchronously when the last reference is removed. +* Process events for the cm_id. If the callback function requests +* that the cm_id be deleted, a flag is set in the cm_id flags to +* indicate that when the last reference is removed, the cm_id is +* to be destroyed. This is necessary to distinguish between an +* object that will be destroyed by the app thread asleep on the +* destroy_comp list vs. an object destroyed here synchronously +* when the last reference is removed. */ static void cm_work_handler (struct work_struct *_work) {
@@ -1024,35 +1020,26 @@ static void cm_work_handler(struct work_struct *_work)int ret =0 ;spin_lock_irqsave (&cm_id_priv->lock, flags); -while (!list_empty (&cm_id_priv->work_list)) { - work =list_first_entry (&cm_id_priv->work_list, - struct iwcm_work, list); -list_del_init (&work->list); - levent = work->event; -put_work (work); -spin_unlock_irqrestore (&cm_id_priv->lock, flags); - -if (!test_bit (IWCM_F_DROP_EVENTS , &cm_id_priv->flags)) { - ret =process_event (cm_id_priv, &levent); -if (ret) { -destroy_cm_id (&cm_id_priv->id); -WARN_ON_ONCE (iwcm_deref_id (cm_id_priv)); - } - }else -pr_debug ("dropping event %d\n" , levent.event); -if (iwcm_deref_id (cm_id_priv)) -return ; -spin_lock_irqsave (&cm_id_priv->lock, flags); - } + levent = work->event; +put_work (work);spin_unlock_irqrestore (&cm_id_priv->lock, flags); + +if (!test_bit (IWCM_F_DROP_EVENTS , &cm_id_priv->flags)) { + ret =process_event (cm_id_priv, &levent); +if (ret) { +destroy_cm_id (&cm_id_priv->id); +WARN_ON_ONCE (iwcm_deref_id (cm_id_priv)); + } + }else +pr_debug ("dropping event %d\n" , levent.event); +if (iwcm_deref_id (cm_id_priv)) +return ; }/* * This function is called on interrupt context. Schedule events on * the iwcm_wq thread to allow callback functions to downcall into -* the CM and/or block. Events are queued to a per-CM_ID -* work_list. If this is the first event on the work_list, the work -* element is also queued on the iwcm_wq thread. +* the CM and/or block. * * Each event holds a reference on the cm_id. Until the last posted * event has been delivered and processed, the cm_id cannot be
@@ -1094,7 +1081,6 @@ static int cm_event_handler(struct iw_cm_id *cm_id, }refcount_inc (&cm_id_priv->refcount); -list_add_tail (&work->list, &cm_id_priv->work_list);queue_work (iwcm_wq, &work->work); out:spin_unlock_irqrestore (&cm_id_priv->lock, flags);
drivers/infiniband/core/iwcm.h +0 -1
@@ -50,7 +50,6 @@ struct iwcm_id_private {struct ib_qp *qp;struct completion destroy_comp;wait_queue_head_t connect_wait; -struct list_head work_list;spinlock_t lock;refcount_t refcount;struct list_head work_free_list;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 464/781 platform/x86: hp-wmi: fix platform profile values for Omen 16-wf1xxx
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krishna Chomal <[email protected]>
[ Upstream commit 8ca7515d3c76a8b629f703ff8301a75f503bcc50 ]
HP Omen 16-wf1xxx (board ID 8C78) currently sends the incorrect Victus-specific thermal profile values via WMI, leading to a logical inconsistency when switching between platform profiles.
The driver currently uses Victus S values:0x00 => Balanced / Low-Power0x01 => Performance
However, Omen Gaming Hub logs / EC register inspection on Windows shows that this board is intended to use:0x30 => Balanced / Low-Power0x31 => Performance
This patch corrects the thermal profile command values to match the values observed from Omen Gaming Hub logs. The performance benchmarks and peak power draw (from both CPU and GPU) show no observable change with this correction (suggesting that the firmware is currently tolerant of the incorrect values). However sending the correct values prevents potential regressions after future firmware updates.
Refactor victus_s_thermal_profile_boards from a list of strings to a dmi_system_id table and move the lookup to module init. The new struct thermal_profile_params is used to store board-specific WMI parameters, allowing the driver to cache these values in a static pointer. This avoids repeated DMI string comparisons and allows marking of DMI table as __initconst.
Testing on HP Omen 16-wf1xxx (board 8C78) confirmed WMI codes 0x30/0x31 are now sent, resolving the logical inconsistency and ensuring the value visible in EC registers match the Windows state for this profile.
--- drivers/platform/x86/hp/hp-wmi.c | 179 ++++++++++++++++++++++--------- 1 file changed, 127 insertions(+), 52 deletions(-)
drivers/platform/x86/hp/hp-wmi.c +127 -52
@@ -53,6 +53,66 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");#define zero_if_sup (tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required +enum hp_thermal_profile_omen_v0 { +HP_OMEN_V0_THERMAL_PROFILE_DEFAULT =0x00 , +HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE =0x01 , +HP_OMEN_V0_THERMAL_PROFILE_COOL =0x02 , +}; + +enum hp_thermal_profile_omen_v1 { +HP_OMEN_V1_THERMAL_PROFILE_DEFAULT =0x30 , +HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE =0x31 , +HP_OMEN_V1_THERMAL_PROFILE_COOL =0x50 , +}; + +enum hp_thermal_profile_omen_flags { +HP_OMEN_EC_FLAGS_TURBO =0x04 , +HP_OMEN_EC_FLAGS_NOTIMER =0x02 , +HP_OMEN_EC_FLAGS_JUSTSET =0x01 , +}; + +enum hp_thermal_profile_victus { +HP_VICTUS_THERMAL_PROFILE_DEFAULT =0x00 , +HP_VICTUS_THERMAL_PROFILE_PERFORMANCE =0x01 , +HP_VICTUS_THERMAL_PROFILE_QUIET =0x03 , +}; + +enum hp_thermal_profile_victus_s { +HP_VICTUS_S_THERMAL_PROFILE_DEFAULT =0x00 , +HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE =0x01 , +}; + +enum hp_thermal_profile { +HP_THERMAL_PROFILE_PERFORMANCE =0x00 , +HP_THERMAL_PROFILE_DEFAULT =0x01 , +HP_THERMAL_PROFILE_COOL =0x02 , +HP_THERMAL_PROFILE_QUIET =0x03 , +}; + +struct thermal_profile_params { +u8 performance; +u8 balanced; +u8 low_power; +}; + +static const struct thermal_profile_params victus_s_thermal_params = { + .performance =HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE , + .balanced =HP_VICTUS_S_THERMAL_PROFILE_DEFAULT , + .low_power =HP_VICTUS_S_THERMAL_PROFILE_DEFAULT , +}; + +static const struct thermal_profile_params omen_v1_thermal_params = { + .performance =HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE , + .balanced =HP_OMEN_V1_THERMAL_PROFILE_DEFAULT , + .low_power =HP_OMEN_V1_THERMAL_PROFILE_DEFAULT , +}; + +/* +* A generic pointer for the currently-active board's thermal profile +* parameters. +*/ +static struct thermal_profile_params *active_thermal_profile_params; +/* DMI board names of devices that should use the omen specific path for * thermal profiles. * This was obtained by taking a look in the windows omen command center
@@ -99,12 +159,40 @@ static const char * const victus_thermal_profile_boards[] = { };/* DMI Board names of Victus 16-r and Victus 16-s laptops */ -static const char *const victus_s_thermal_profile_boards[] = { -"8BBE" ,"8BD4" ,"8BD5" , -"8C78" ,"8C99" ,"8C9C" , -"8D41" , +static const struct dmi_system_id victus_s_thermal_profile_boards[]__initconst = { + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8BBE" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8BD4" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8BD5" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8C78" ) }, + .driver_data = (void *)&omen_v1_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8C99" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8C9C" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + { + .matches = {DMI_MATCH (DMI_BOARD_NAME ,"8D41" ) }, + .driver_data = (void *)&victus_s_thermal_params, + }, + {}, }; +static bool is_victus_s_board; +enum hp_wmi_radio {HPWMI_WIFI =0x0 ,HPWMI_BLUETOOTH =0x1 ,
@@ -225,42 +313,6 @@ enum hp_wireless2_bits { HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, }; -enum hp_thermal_profile_omen_v0 { -HP_OMEN_V0_THERMAL_PROFILE_DEFAULT =0x00 , -HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE =0x01 , -HP_OMEN_V0_THERMAL_PROFILE_COOL =0x02 , -}; - -enum hp_thermal_profile_omen_v1 { -HP_OMEN_V1_THERMAL_PROFILE_DEFAULT =0x30 , -HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE =0x31 , -HP_OMEN_V1_THERMAL_PROFILE_COOL =0x50 , -}; - -enum hp_thermal_profile_omen_flags { -HP_OMEN_EC_FLAGS_TURBO =0x04 , -HP_OMEN_EC_FLAGS_NOTIMER =0x02 , -HP_OMEN_EC_FLAGS_JUSTSET =0x01 , -}; - -enum hp_thermal_profile_victus { -HP_VICTUS_THERMAL_PROFILE_DEFAULT =0x00 , -HP_VICTUS_THERMAL_PROFILE_PERFORMANCE =0x01 , -HP_VICTUS_THERMAL_PROFILE_QUIET =0x03 , -}; - -enum hp_thermal_profile_victus_s { -HP_VICTUS_S_THERMAL_PROFILE_DEFAULT =0x00 , -HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE =0x01 , -}; - -enum hp_thermal_profile { -HP_THERMAL_PROFILE_PERFORMANCE =0x00 , -HP_THERMAL_PROFILE_DEFAULT =0x01 , -HP_THERMAL_PROFILE_COOL =0x02 , -HP_THERMAL_PROFILE_QUIET =0x03 , -}; -#define IS_HWBLOCKED (x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)#define IS_SWBLOCKED (x) !(x & HPWMI_POWER_SOFT)
@@ -1581,15 +1633,8 @@ static int platform_profile_victus_set_ec(enum platform_profile_option profile)static bool is_victus_s_thermal_profile (void ) { -const char *board_name; - - board_name =dmi_get_system_info (DMI_BOARD_NAME ); -if (!board_name) -return false; - -return match_string (victus_s_thermal_profile_boards, -ARRAY_SIZE (victus_s_thermal_profile_boards), - board_name) >=0 ; +/* Initialised in driver init, hence safe to use here */ +return is_victus_s_board; } static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
@@ -1672,25 +1717,30 @@ static int victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2)static int platform_profile_victus_s_set_ec (enum platform_profile_option profile) { +struct thermal_profile_params *params;bool gpu_ctgp_enable, gpu_ppab_enable;u8 gpu_dstate;/* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */ int err, tp; + params = active_thermal_profile_params; +if (!params) +return -ENODEV ; +switch (profile) {case PLATFORM_PROFILE_PERFORMANCE : - tp =HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE ; + tp = params->performance; gpu_ctgp_enable = true; gpu_ppab_enable = true; gpu_dstate =1 ;break ;case PLATFORM_PROFILE_BALANCED : - tp =HP_VICTUS_S_THERMAL_PROFILE_DEFAULT ; + tp = params->balanced; gpu_ctgp_enable = false; gpu_ppab_enable = true; gpu_dstate =1 ;break ;case PLATFORM_PROFILE_LOW_POWER : - tp =HP_VICTUS_S_THERMAL_PROFILE_DEFAULT ; + tp = params->low_power; gpu_ctgp_enable = false; gpu_ppab_enable = false; gpu_dstate =1 ;
@@ -2227,6 +2277,26 @@ static int hp_wmi_hwmon_init(void)return 0 ; } +static void __init setup_active_thermal_profile_params (void ) +{ +const struct dmi_system_id *id; + +/* +* Currently only victus_s devices use the +* active_thermal_profile_params +*/ + id =dmi_first_match (victus_s_thermal_profile_boards); +if (id) { +/* +* Marking this boolean is required to ensure that +* is_victus_s_thermal_profile() behaves like a valid +* wrapper. +*/ + is_victus_s_board = true; + active_thermal_profile_params = id->driver_data; + } +} +static int __init hp_wmi_init (void ) {int event_capable =wmi_has_guid (HPWMI_EVENT_GUID );
@@ -2254,6 +2324,11 @@ static int __init hp_wmi_init(void) goto err_destroy_input; } +/* +* Setup active board's thermal profile parameters before +* starting platform driver probe. +*/ +setup_active_thermal_profile_params (); err =platform_driver_probe (&hp_wmi_driver, hp_wmi_bios_setup);if (err) goto err_unregister_device;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 465/781 tools/power/x86/intel-speed-select: Fix file descriptor leak in isolate_cpus()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Malaya Kumar Rout <[email protected]>
[ Upstream commit 56c17ee151c6e1a73d77e15b82a8e2130cd8dd16 ]
The file descriptor opened in isolate_cpus() when (!level) is true was not being closed before returning, causing a file descriptor leak in both the error path and the success path.
When write() fails at line 950, the function returns at line 953 without closing the file descriptor. Similarly, on success, the function returns at line 956 without closing the file descriptor.
Add close(fd) calls before both return statements to fix the resource leak. This follows the same pattern used elsewhere in the same function where file descriptors are properly closed before returning (see lines 1005 and 1027).
--- tools/power/x86/intel-speed-select/isst-config.c | 2 ++ 1 file changed, 2 insertions(+)
tools/power/x86/intel-speed-select/isst-config.c +2 -0
@@ -950,9 +950,11 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev ret =write (fd,"member" ,strlen ("member" ));if (ret ==-1 ) {printf ("Can't update to member\n" ); +close (fd);return ret; } +close (fd);return 0 ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 466/781 RDMA/mlx5: Fix UMR hang in LAG error state unload
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chiara Meiohas <[email protected]>
[ Upstream commit ebc2164a4cd4314503f1a0c8e7aaf76d7e5fa211 ]
During firmware reset in LAG mode, a race condition causes the driver to hang indefinitely while waiting for UMR completion during device unload. See [1].
In LAG mode the bond device is only registered on the master, so it never sees sys_error events from the slave.
During firmware reset this causes UMR waits to hang forever on unload as the slave is dead but the master hasn't entered error state yet, so UMR posts succeed but completions never arrive.
Fix this by adding a sys_error notifier that gets registered before MLX5_IB_STAGE_IB_REG and stays alive until after ib_unregister_device(). This ensures error events reach the bond device throughout teardown.
[1]
Call Trace: __schedule+0x2bd/0x760 schedule+0x37/0xa0 schedule_preempt_disabled+0xa/0x10 __mutex_lock.isra.6+0x2b5/0x4a0 __mlx5_ib_dereg_mr+0x606/0x870 [mlx5_ib] ? __xa_erase+0x4a/0xa0 ? _cond_resched+0x15/0x30 ? wait_for_completion+0x31/0x100 ib_dereg_mr_user+0x48/0xc0 [ib_core] ? rdmacg_uncharge_hierarchy+0xa0/0x100 destroy_hw_idr_uobject+0x20/0x50 [ib_uverbs] uverbs_destroy_uobject+0x37/0x150 [ib_uverbs] __uverbs_cleanup_ufile+0xda/0x140 [ib_uverbs] uverbs_destroy_ufile_hw+0x3a/0xf0 [ib_uverbs] ib_uverbs_remove_one+0xc3/0x140 [ib_uverbs] remove_client_context+0x8b/0xd0 [ib_core] disable_device+0x8c/0x130 [ib_core] __ib_unregister_device+0x10d/0x180 [ib_core] ib_unregister_device+0x21/0x30 [ib_core] __mlx5_ib_remove+0x1e4/0x1f0 [mlx5_ib] auxiliary_bus_remove+0x1e/0x30 device_release_driver_internal+0x103/0x1f0 bus_remove_device+0xf7/0x170 device_del+0x181/0x410 mlx5_rescan_drivers_locked.part.10+0xa9/0x1d0 [mlx5_core] mlx5_disable_lag+0x253/0x260 [mlx5_core] mlx5_lag_disable_change+0x89/0xc0 [mlx5_core] mlx5_eswitch_disable+0x67/0xa0 [mlx5_core] mlx5_unload+0x15/0xd0 [mlx5_core] mlx5_unload_one+0x71/0xc0 [mlx5_core] mlx5_sync_reset_reload_work+0x83/0x100 [mlx5_core] process_one_work+0x1a7/0x360 worker_thread+0x30/0x390 ? create_worker+0x1a0/0x1a0 kthread+0x116/0x130 ? kthread_flush_work_fn+0x10/0x10 ret_from_fork+0x22/0x40
--- drivers/infiniband/hw/mlx5/main.c | 75 ++++++++++++++++++++++++---- drivers/infiniband/hw/mlx5/mlx5_ib.h | 2 + 2 files changed, 68 insertions(+), 9 deletions(-)
drivers/infiniband/hw/mlx5/main.c +66 -9
@@ -2878,7 +2878,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work) container_of(_work, struct mlx5_ib_event_work, work);struct mlx5_ib_dev *ibdev;struct ib_event ibev; -bool fatal = false;if (work->is_slave) { ibdev =mlx5_ib_get_ibdev_from_mpi (work->mpi);
@@ -2889,12 +2888,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work) }switch (work->event) { -case MLX5_DEV_EVENT_SYS_ERROR : - ibev.event =IB_EVENT_DEVICE_FATAL ; -mlx5_ib_handle_internal_error (ibdev); - ibev.element.port_num = (u8 )(unsigned long )work->param; - fatal = true; -break ;case MLX5_EVENT_TYPE_PORT_CHANGE :if (handle_port_change (ibdev, work->param, &ibev)) goto out;
@@ -2916,8 +2909,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)if (ibdev->ib_active)ib_dispatch_event (&ibev); -if (fatal) - ibdev->ib_active = false; out:kfree (work); }
@@ -2961,6 +2952,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,return NOTIFY_OK ; } +static void mlx5_ib_handle_sys_error_event (struct work_struct *_work) +{ +struct mlx5_ib_event_work *work = + container_of(_work, struct mlx5_ib_event_work, work); +struct mlx5_ib_dev *ibdev = work->dev; +struct ib_event ibev; + + ibev.event =IB_EVENT_DEVICE_FATAL ; +mlx5_ib_handle_internal_error (ibdev); + ibev.element.port_num = (u8 )(unsigned long )work->param; + ibev.device = &ibdev->ib_dev; + +if (!rdma_is_port_valid (&ibdev->ib_dev, ibev.element.port_num)) { +mlx5_ib_warn (ibdev,"warning: event on port %d\n" , ibev.element.port_num); + goto out; + } + +if (ibdev->ib_active) +ib_dispatch_event (&ibev); + + ibdev->ib_active = false; +out: +kfree (work); +} + +static int mlx5_ib_sys_error_event (struct notifier_block *nb, +unsigned long event,void *param) +{ +struct mlx5_ib_event_work *work; + +if (event !=MLX5_DEV_EVENT_SYS_ERROR ) +return NOTIFY_DONE ; + + work =kmalloc (sizeof (*work),GFP_ATOMIC ); +if (!work) +return NOTIFY_DONE ; + +INIT_WORK (&work->work, mlx5_ib_handle_sys_error_event); + work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events); + work->is_slave = false; + work->param = param; + work->event = event; + +queue_work (mlx5_ib_event_wq, &work->work); + +return NOTIFY_OK ; +} + +static int mlx5_ib_stage_sys_error_notifier_init (struct mlx5_ib_dev *dev) +{ + dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event; +mlx5_notifier_register (dev->mdev, &dev->sys_error_events); +return 0 ; +} + +static void mlx5_ib_stage_sys_error_notifier_cleanup (struct mlx5_ib_dev *dev) +{ +mlx5_notifier_unregister (dev->mdev, &dev->sys_error_events); +} +static int mlx5_ib_get_plane_num (struct mlx5_core_dev *mdev,u8 *num_plane) {struct mlx5_hca_vport_context vport_ctx;
@@ -4811,6 +4862,9 @@ static const struct mlx5_ib_profile pf_profile = {STAGE_CREATE (MLX5_IB_STAGE_WHITELIST_UID , mlx5_ib_devx_init, mlx5_ib_devx_cleanup), +STAGE_CREATE (MLX5_IB_STAGE_SYS_ERROR_NOTIFIER , + mlx5_ib_stage_sys_error_notifier_init, + mlx5_ib_stage_sys_error_notifier_cleanup),STAGE_CREATE (MLX5_IB_STAGE_IB_REG , mlx5_ib_stage_ib_reg_init, mlx5_ib_stage_ib_reg_cleanup),
@@ -4868,6 +4922,9 @@ const struct mlx5_ib_profile raw_eth_profile = {STAGE_CREATE (MLX5_IB_STAGE_WHITELIST_UID , mlx5_ib_devx_init, mlx5_ib_devx_cleanup), +STAGE_CREATE (MLX5_IB_STAGE_SYS_ERROR_NOTIFIER , + mlx5_ib_stage_sys_error_notifier_init, + mlx5_ib_stage_sys_error_notifier_cleanup),STAGE_CREATE (MLX5_IB_STAGE_IB_REG , mlx5_ib_stage_ib_reg_init, mlx5_ib_stage_ib_reg_cleanup),
drivers/infiniband/hw/mlx5/mlx5_ib.h +2 -0
@@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {MLX5_IB_STAGE_BFREG ,MLX5_IB_STAGE_PRE_IB_REG_UMR ,MLX5_IB_STAGE_WHITELIST_UID , +MLX5_IB_STAGE_SYS_ERROR_NOTIFIER ,MLX5_IB_STAGE_IB_REG ,MLX5_IB_STAGE_DEVICE_NOTIFIER , MLX5_IB_STAGE_POST_IB_REG_UMR,
@@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {/* protect accessing data_direct_dev */ struct mutex data_direct_lock;struct notifier_block mdev_events; +struct notifier_block sys_error_events;struct notifier_block lag_events;int num_ports;/* serialize update of capability mask
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 467/781 IB/mlx5: Fix port speed query for representors
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Or Har-Toov <[email protected]>
[ Upstream commit 18ea78e2ae83d1d86a72d21d9511927e57e2c0e1 ]
When querying speed information for a representor in switchdev mode, the code previously used the first device in the eswitch, which may not match the device that actually owns the representor. In setups such as multi-port eswitch or LAG, this led to incorrect port attributes being reported.
Fix this by retrieving the correct core device from the representor's eswitch before querying its port attributes.
--- drivers/infiniband/hw/mlx5/main.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
drivers/infiniband/hw/mlx5/main.c +14 -6
@@ -561,12 +561,20 @@ static int mlx5_query_port_roce(struct ib_device *device, u32 port_num,* of an error it will still be zeroed out. * Use native port in case of reps */ -if (dev->is_rep) - err =mlx5_query_port_ptys (mdev, out,sizeof (out),MLX5_PTYS_EN , -1 ,0 ); -else - err =mlx5_query_port_ptys (mdev, out,sizeof (out),MLX5_PTYS_EN , - mdev_port_num,0 ); +if (dev->is_rep) { +struct mlx5_eswitch_rep *rep; + + rep = dev->port[port_num -1 ].rep; +if (rep) { + mdev =mlx5_eswitch_get_core_dev (rep->esw); +WARN_ON (!mdev); + } + mdev_port_num =1 ; + } + + err =mlx5_query_port_ptys (mdev, out,sizeof (out),MLX5_PTYS_EN , + mdev_port_num,0 ); +if (err) goto out; ext = !!MLX5_GET_ETH_PROTO (ptys_reg, out, true, eth_proto_capability);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 468/781 mtd: rawnand: cadence: Fix return type of CDMA send-and-wait helper
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alok Tiwari <[email protected]>
[ Upstream commit 6d8226cbbf124bb5613b532216b74c886a4361b7 ]
cadence_nand_cdma_send_and_wait() propagates negative errno values from cadence_nand_cdma_send(), returns -ETIMEDOUT on failure and -EIO when the CDMA engine reports a command failure.
However, it is declared as u32, causing error codes to wrap. Change the return type to int to correctly propagate errors.
--- drivers/mtd/nand/raw/cadence-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mtd/nand/raw/cadence-nand-controller.c +1 -1
@@ -1066,7 +1066,7 @@ static int cadence_nand_cdma_send(struct cdns_nand_ctrl *cdns_ctrl, }/* Send SDMA command and wait for finish. */ -static u32 +static int cadence_nand_cdma_send_and_wait (struct cdns_nand_ctrl *cdns_ctrl,u8 thread) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 469/781 mtd: intel-dg: Fix accessing regions before setting nregions
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Usyskin <[email protected]>
[ Upstream commit 779c59274d03cc5c07237a2c845dfb71cff77705 ]
The regions array is counted by nregions, but it's set only after accessing it:
[] UBSAN: array-index-out-of-bounds in drivers/mtd/devices/mtd_intel_dg.c:750:15 [] index 0 is out of range for type '<unknown> [*]'
Fix it by also fixing an undesired behavior: the loop silently ignores ENOMEM and continues setting the other entries.
--- drivers/mtd/devices/mtd_intel_dg.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/mtd/devices/mtd_intel_dg.c +6 -3
@@ -770,6 +770,7 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,kref_init (&nvm->refcnt);mutex_init (&nvm->lock); + nvm->nregions = nregions;for (n =0 , i =0 ; i <INTEL_DG_NVM_REGIONS ; i++) {if (!invm->regions[i].name)
@@ -777,13 +778,15 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,char *name =kasprintf (GFP_KERNEL ,"%s.%s" ,dev_name (&aux_dev->dev), invm->regions[i].name); -if (!name) -continue ; +if (!name) { + ret = -ENOMEM ; + goto err; + } + nvm->regions[n].name = name; nvm->regions[n].id = i; n++; } - nvm->nregions = n;/* in case where kasprintf fail */ ret =devm_pm_runtime_enable (device);if (ret <0 ) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 470/781 vfio/pci: Lock upstream bridge for vfio_pci_core_disable()
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anthony Pighin (Nokia) <[email protected]>
[ Upstream commit 962ae6892d8bd208b2d1e2b358f07551ddc8d32f ]
The commit 7e89efc6e9e4 ("Lock upstream bridge for pci_reset_function()") added locking of the upstream bridge to the reset function. To catch paths that are not properly locked, the commit 920f6468924f ("Warn on missing cfg_access_lock during secondary bus reset") added a warning if the PCI configuration space was not locked during a secondary bus reset request.
When a VFIO PCI device is released from userspace ownership, an attempt to reset the PCI device function may be made. If so, and the upstream bridge is not locked, the release request results in a warning:
pcieport 0000:00:00.0: unlocked secondary bus reset via:
pci_reset_bus_function+0x188/0x1b8
Add missing upstream bridge locking to vfio_pci_core_disable().
--- drivers/vfio/pci/vfio_pci_core.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-)
drivers/vfio/pci/vfio_pci_core.c +13 -4
@@ -588,6 +588,7 @@ EXPORT_SYMBOL_GPL(vfio_pci_core_enable);void vfio_pci_core_disable (struct vfio_pci_core_device *vdev) { +struct pci_dev *bridge;struct pci_dev *pdev = vdev->pdev;struct vfio_pci_dummy_resource *dummy_res, *tmp;struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp;
@@ -694,12 +695,20 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)* We can not use the "try" reset interface here, which will * overwrite the previously restored configuration information. */ -if (vdev->reset_works &&pci_dev_trylock (pdev)) { -if (!__pci_reset_function_locked (pdev)) - vdev->needs_reset = false; -pci_dev_unlock (pdev); +if (vdev->reset_works) { + bridge =pci_upstream_bridge (pdev); +if (bridge && !pci_dev_trylock (bridge)) + goto out_restore_state; +if (pci_dev_trylock (pdev)) { +if (!__pci_reset_function_locked (pdev)) + vdev->needs_reset = false; +pci_dev_unlock (pdev); + } +if (bridge) +pci_dev_unlock (bridge); } +out_restore_state:pci_restore_state (pdev); out:pci_disable_device (pdev);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 471/781 platform/x86/amd/pmf: Prevent TEE errors after hibernate
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shyam Sundar S K <[email protected]>
[ Upstream commit 48d229c7047128dd52eaf863881bb3e62b5896e5 ]
After resuming from hibernate, TEE commands can time out and cause PSP disables. Fix this by reinitializing the Trusted Application (TA) and cancelling the pb workqueue in the hibernate callbacks to avoid these errors.
ccp 0000:c4:00.2: tee: command 0x5 timed out, disabling PSP amd-pmf AMDI0107:00: TEE enact cmd failed. err: ffff000e, ret:0 amd-pmf AMDI0107:00: TEE enact cmd failed. err: ffff000e, ret:0 amd-pmf AMDI0107:00: TEE enact cmd failed. err: ffff000e, ret:0
--- drivers/platform/x86/amd/pmf/core.c | 62 ++++++++++++++++++++++++++- drivers/platform/x86/amd/pmf/pmf.h | 10 +++++ drivers/platform/x86/amd/pmf/tee-if.c | 12 ++---- 3 files changed, 74 insertions(+), 10 deletions(-)
drivers/platform/x86/amd/pmf/core.c +61 -1
@@ -314,6 +314,61 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)return 0 ; } +static int amd_pmf_reinit_ta (struct amd_pmf_dev *pdev) +{ +bool status; +int ret, i; + +for (i =0 ; i <ARRAY_SIZE (amd_pmf_ta_uuid); i++) { + ret =amd_pmf_tee_init (pdev, &amd_pmf_ta_uuid[i]); +if (ret) { +dev_err (pdev->dev,"TEE init failed for UUID[%d] ret: %d\n" , i, ret); +return ret; + } + + ret =amd_pmf_start_policy_engine (pdev); +dev_dbg (pdev->dev,"start policy engine ret: %d (UUID idx: %d)\n" , ret, i); + status = ret ==TA_PMF_TYPE_SUCCESS ; +if (status) +break ; +amd_pmf_tee_deinit (pdev); + } + +return 0 ; +} + +static int amd_pmf_restore_handler (struct device *dev) +{ +struct amd_pmf_dev *pdev =dev_get_drvdata (dev); +int ret; + +if (pdev->buf) { + ret =amd_pmf_set_dram_addr (pdev, false); +if (ret) +return ret; + } + +if (pdev->smart_pc_enabled) +amd_pmf_reinit_ta (pdev); + +return 0 ; +} + +static int amd_pmf_freeze_handler (struct device *dev) +{ +struct amd_pmf_dev *pdev =dev_get_drvdata (dev); + +if (!pdev->smart_pc_enabled) +return 0 ; + +cancel_delayed_work_sync (&pdev->pb_work); +/* Clear all TEE resources */ +amd_pmf_tee_deinit (pdev); + pdev->session_id =0 ; + +return 0 ; +} +static int amd_pmf_suspend_handler (struct device *dev) {struct amd_pmf_dev *pdev =dev_get_drvdata (dev);
@@ -347,7 +402,12 @@ static int amd_pmf_resume_handler(struct device *dev)return 0 ; } -static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, amd_pmf_suspend_handler, amd_pmf_resume_handler); +static const struct dev_pm_ops amd_pmf_pm = { + .suspend = amd_pmf_suspend_handler, + .resume = amd_pmf_resume_handler, + .freeze = amd_pmf_freeze_handler, + .restore = amd_pmf_restore_handler, +};static void amd_pmf_init_features (struct amd_pmf_dev *dev) {
drivers/platform/x86/amd/pmf/pmf.h +10 -0
@@ -129,6 +129,12 @@ struct cookie_header {typedef void (*apmf_event_handler_t )(acpi_handle handle,u32 event,void *data); +static const uuid_t amd_pmf_ta_uuid[]__used = {UUID_INIT (0xd9b39bf2 ,0x66bd ,0x4154 ,0xaf ,0xb8 , +0x8a ,0xcc ,0x2b ,0x2b ,0x60 ,0xd6 ), +UUID_INIT (0x6fd93b77 ,0x3fb8 ,0x524d ,0xb1 ,0x2d , +0xc5 ,0x29 ,0xb1 ,0x3d ,0x85 ,0x43 ), + }; +/* APTS PMF BIOS Interface */ struct amd_pmf_apts_output {u16 table_version;
@@ -895,4 +901,8 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tabvoid amd_pmf_dump_ta_inputs (struct amd_pmf_dev *dev,struct ta_pmf_enact_table *in);int amd_pmf_invoke_cmd_enact (struct amd_pmf_dev *dev); +int amd_pmf_tee_init (struct amd_pmf_dev *dev,const uuid_t *uuid); +void amd_pmf_tee_deinit (struct amd_pmf_dev *dev); +int amd_pmf_start_policy_engine (struct amd_pmf_dev *dev); +#endif /* PMF_H */
drivers/platform/x86/amd/pmf/tee-if.c +3 -9
@@ -27,12 +27,6 @@ module_param(pb_side_load, bool, 0444);MODULE_PARM_DESC (pb_side_load,"Sideload policy binaries debug policy failures" );#endif -static const uuid_t amd_pmf_ta_uuid[] = {UUID_INIT (0xd9b39bf2 ,0x66bd ,0x4154 ,0xaf ,0xb8 ,0x8a , -0xcc ,0x2b ,0x2b ,0x60 ,0xd6 ), -UUID_INIT (0x6fd93b77 ,0x3fb8 ,0x524d ,0xb1 ,0x2d ,0xc5 , -0x29 ,0xb1 ,0x3d ,0x85 ,0x43 ), - }; -static const char *amd_pmf_uevent_as_str (unsigned int state) {switch (state) {
@@ -324,7 +318,7 @@ static void amd_pmf_invoke_cmd(struct work_struct *work)schedule_delayed_work (&dev->pb_work,msecs_to_jiffies (pb_actions_ms)); } -static int amd_pmf_start_policy_engine (struct amd_pmf_dev *dev) +int amd_pmf_start_policy_engine (struct amd_pmf_dev *dev) {struct cookie_header *header;int res;
@@ -480,7 +474,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)return 0 ; } -static int amd_pmf_tee_init (struct amd_pmf_dev *dev,const uuid_t *uuid) +int amd_pmf_tee_init (struct amd_pmf_dev *dev,const uuid_t *uuid) {u32 size;int ret;
@@ -528,7 +522,7 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)return ret; } -static void amd_pmf_tee_deinit (struct amd_pmf_dev *dev) +void amd_pmf_tee_deinit (struct amd_pmf_dev *dev) {if (!dev->tee_ctx)return ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 472/781 crypto: ccp - Declare PSP dead if PSP_CMD_TEE_RING_INIT fails
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello (AMD) <[email protected]>
[ Upstream commit 5e599d7871bf852e94e8aa08b99724635f2cbf96 ]
tee_init_ring() only declares PSP dead if the command times out. If there is any other failure it is still considered fatal though. Set psp_dead for other failures as well.
--- drivers/crypto/ccp/tee-dev.c | 1 + 1 file changed, 1 insertion(+)
drivers/crypto/ccp/tee-dev.c +1 -0
@@ -125,6 +125,7 @@ static int tee_init_ring(struct psp_tee_device *tee)dev_err (tee->dev,"tee: ring init command failed (%#010lx)\n" ,FIELD_GET (PSP_CMDRESP_STS , reg));tee_free_ring (tee); + psp_dead = true; ret = -EIO ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 473/781 crypto: ccp - Add an S4 restore flow
In reply to Greg Kroah-Hartman6.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello (AMD) <[email protected]>
[ Upstream commit 0ba2035026d0ab6c7c7e65ad8b418dc73d5700d9 ]
The system will have lost power during S4. The ring used for TEE communications needs to be initialized before use.
--- drivers/crypto/ccp/psp-dev.c | 11 +++++++++++ drivers/crypto/ccp/sp-dev.c | 12 ++++++++++++ drivers/crypto/ccp/sp-dev.h | 3 +++ drivers/crypto/ccp/sp-pci.c | 16 +++++++++++++++- drivers/crypto/ccp/tee-dev.c | 5 +++++ drivers/crypto/ccp/tee-dev.h | 1 + 6 files changed, 47 insertions(+), 1 deletion(-)
drivers/crypto/ccp/psp-dev.c +11 -0
@@ -351,6 +351,17 @@ struct psp_device *psp_get_master_device(void)return sp ? sp->psp_data :NULL ; } +int psp_restore (struct sp_device *sp) +{ +struct psp_device *psp = sp->psp_data; +int ret =0 ; + +if (psp->tee_data) + ret =tee_restore (psp); + +return ret; +} +void psp_pci_init (void ) { psp_master =psp_get_master_device ();
drivers/crypto/ccp/sp-dev.c +12 -0
@@ -230,6 +230,18 @@ int sp_resume(struct sp_device *sp)return 0 ; } +int sp_restore (struct sp_device *sp) +{ +if (sp->psp_data) { +int ret =psp_restore (sp); + +if (ret) +return ret; + } + +return sp_resume (sp); +} +struct sp_device *sp_get_psp_master_device (void ) {struct sp_device *i, *ret =NULL ;
drivers/crypto/ccp/sp-dev.h +3 -0
@@ -141,6 +141,7 @@ void sp_destroy(struct sp_device *sp);int sp_suspend (struct sp_device *sp);int sp_resume (struct sp_device *sp); +int sp_restore (struct sp_device *sp);int sp_request_ccp_irq (struct sp_device *sp,irq_handler_t handler,const char *name,void *data);void sp_free_ccp_irq (struct sp_device *sp,void *data);
@@ -174,6 +175,7 @@ int psp_dev_init(struct sp_device *sp);void psp_pci_init (void );void psp_dev_destroy (struct sp_device *sp);void psp_pci_exit (void ); +int psp_restore (struct sp_device *sp);#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
@@ -181,6 +183,7 @@ static inline int psp_dev_init(struct sp_device *sp) { return 0; }static inline void psp_pci_init (void ) { }static inline void psp_dev_destroy (struct sp_device *sp) { }static inline void psp_pci_exit (void ) { } +static inline int psp_restore (struct sp_device *sp) {return 0 ; }#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
drivers/crypto/ccp/sp-pci.c +15 -1
@@ -353,6 +353,13 @@ static int __maybe_unused sp_pci_resume(struct device *dev)return sp_resume (sp); } +static int __maybe_unused sp_pci_restore (struct device *dev) +{ +struct sp_device *sp =dev_get_drvdata (dev); + +return sp_restore (sp); +} +#ifdef CONFIG_CRYPTO_DEV_SP_PSP static const struct sev_vdata sevv1 = { .cmdresp_reg = 0x10580, /* C2PMSG_32 */
@@ -563,7 +570,14 @@ static const struct pci_device_id sp_pci_table[] = { };MODULE_DEVICE_TABLE (pci, sp_pci_table); -static SIMPLE_DEV_PM_OPS(sp_pci_pm_ops, sp_pci_suspend, sp_pci_resume); +static const struct dev_pm_ops sp_pci_pm_ops = { + .suspend =pm_sleep_ptr (sp_pci_suspend), + .resume =pm_sleep_ptr (sp_pci_resume), + .freeze =pm_sleep_ptr (sp_pci_suspend), + .thaw =pm_sleep_ptr (sp_pci_resume), + .poweroff =pm_sleep_ptr (sp_pci_suspend), + .restore_early =pm_sleep_ptr (sp_pci_restore), +};static struct pci_driver sp_pci_driver = { .name ="ccp" ,
drivers/crypto/ccp/tee-dev.c +5 -0
@@ -366,3 +366,8 @@ int psp_check_tee_status(void)return 0 ; }EXPORT_SYMBOL (psp_check_tee_status); + +int tee_restore (struct psp_device *psp) +{ +return tee_init_ring (psp->tee_data); +}
drivers/crypto/ccp/tee-dev.h +1 -0
@@ -111,5 +111,6 @@ struct tee_ring_cmd {int tee_dev_init (struct psp_device *psp);void tee_dev_destroy (struct psp_device *psp); +int tee_restore (struct psp_device *psp);#endif /* __TEE_DEV_H__ */
Signature · 1 line
2.51.0