From 145d4f2ae685cee28589102fb99071d9f6b76fe0 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 17 Jun 2024 07:30:32 -0400 Subject: [PATCH] Fixes for 6.1 Signed-off-by: Sasha Levin --- ...h-msg_peek-loops-if-the-first-unread.patch | 85 ++ ...fix-rejecting-l2cap_conn_param_updat.patch | 108 ++ ...ogging-of-firmware-messages-in-case-.patch | 47 + ...utput-string-to-cachefiles_obj_-get-.patch | 40 + ...estore-command-to-recover-inflight-o.patch | 94 ++ ...pin_lock-for-cachefiles_ondemand_inf.patch | 138 ++ ...-exposing-anon_fd-until-after-copy_t.patch | 164 +++ ...ct-ondemand-info-field-from-cachefil.patch | 206 +++ ...lab-use-after-free-in-cachefiles_ond.patch | 189 +++ ...e-after-free-in-cachefiles_ond.patch-24352 | 143 ++ ...-all-requests-after-setting-cachefil.patch | 71 + ...iles-introduce-object-ondemand-state.patch | 145 ++ ...-get-a-new-anonymous-fd-if-ondemand_.patch | 166 +++ ...e-err_put_fd-label-in-cachefiles_ond.patch | 108 ++ ...e-requests-from-xarray-during-flushi.patch | 62 + ...d-an-open-request-if-the-read-reques.patch | 272 ++++ ...not-register-clkdevs-for-prci-clocks.patch | 62 + ...sing-vmalloc.h-for-tools-testing-cxl.patch | 55 + ...-fix-runtime-warning-on-panel-bridge.patch | 51 + ...omeda-check-for-error-valued-pointer.patch | 37 + ...sabled-should-not-effect-stdu-memory.patch | 45 + ...r-modes-which-exceed-graphics-memory.patch | 106 ++ ...the-framebuffer-code-to-drm-fb-helpe.patch | 1294 +++++++++++++++++ ...tor-drm-connector-probing-for-displa.patch | 445 ++++++ ...e-stdu-logic-from-generic-mode_valid.patch | 93 ++ ...rect-inner-network-header-offset-whe.patch | 106 ++ ...tqmx86-convert-to-immutable-irq_chip.patch | 108 ++ ...broken-irq_type_edge_both-interrupt-.patch | 114 ++ ...pio-tqmx86-fix-typo-in-kconfig-label.patch | 38 + ...oduce-shadow-register-for-gpio-outpu.patch | 89 ++ ...ve-unneeded-call-to-platform_set_drv.patch | 40 + ...e-irq-trigger-type-and-unmask-status.patch | 137 ++ ...levant-gso-type-bits-when-processing.patch | 86 ++ ...ove-unnecessary-warn_on-in-implement.patch | 67 + ...fix-memory-leak-in-logi_dj_recv_swit.patch | 41 + ...mmu-amd-fix-sysfs-leak-in-iommu-init.patch | 47 + .../ionic-fix-use-after-netif_napi_del.patch | 97 ++ ...a-null-pointer-handling-path-in-lio_.patch | 69 + ...ix-suspicious-rcu-usage-in-br_mst_se.patch | 40 + ...ass-vlan-group-directly-to-br_mst_vl.patch | 72 + ...d_resched-to-hns3-ring-buffer-init-p.patch | 64 + ...nel-crash-problem-in-concurrent-scen.patch | 84 ++ ...-rt-cache-flush-via-sysctl-using-a-p.patch | 53 + ...atures-validation-check-for-tunneled.patch | 53 + ...opnotsupp-error-code-instead-of-enot.patch | 47 + ...ays-call-sfp_sm_mod_remove-on-remove.patch | 45 + ...ce-priv-speed-with-the-porttransmitr.patch | 101 ++ ...fix-race-between-namespace-cleanup-a.patch | 289 ++++ ...owlabel-flow-key-when-re-routing-man.patch | 41 + ...-when-testing-for-nfs_fsdata_blocked.patch | 151 ++ ...-rootpath-check-in-fs_location-query.patch | 75 + ...ropagate-status-from-id-override-fun.patch | 46 + ...l-smbios-fix-wrong-token-data-in-sys.patch | 226 +++ ...ace-fix-to-check-required-event-file.patch | 40 + queue-6.1/series | 56 + ...-proper-error-from-gss_wrap_req_priv.patch | 40 + ...tcp-fix-race-in-tcp_v6_syn_recv_sock.patch | 54 + 57 files changed, 6842 insertions(+) create mode 100644 queue-6.1/af_unix-read-with-msg_peek-loops-if-the-first-unread.patch create mode 100644 queue-6.1/bluetooth-l2cap-fix-rejecting-l2cap_conn_param_updat.patch create mode 100644 queue-6.1/bnxt_en-adjust-logging-of-firmware-messages-in-case-.patch create mode 100644 queue-6.1/cachefiles-add-output-string-to-cachefiles_obj_-get-.patch create mode 100644 queue-6.1/cachefiles-add-restore-command-to-recover-inflight-o.patch create mode 100644 queue-6.1/cachefiles-add-spin_lock-for-cachefiles_ondemand_inf.patch create mode 100644 queue-6.1/cachefiles-defer-exposing-anon_fd-until-after-copy_t.patch create mode 100644 queue-6.1/cachefiles-extract-ondemand-info-field-from-cachefil.patch create mode 100644 queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch create mode 100644 queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch-24352 create mode 100644 queue-6.1/cachefiles-flush-all-requests-after-setting-cachefil.patch create mode 100644 queue-6.1/cachefiles-introduce-object-ondemand-state.patch create mode 100644 queue-6.1/cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch create mode 100644 queue-6.1/cachefiles-remove-err_put_fd-label-in-cachefiles_ond.patch create mode 100644 queue-6.1/cachefiles-remove-requests-from-xarray-during-flushi.patch create mode 100644 queue-6.1/cachefiles-resend-an-open-request-if-the-read-reques.patch create mode 100644 queue-6.1/clk-sifive-do-not-register-clkdevs-for-prci-clocks.patch create mode 100644 queue-6.1/cxl-test-add-missing-vmalloc.h-for-tools-testing-cxl.patch create mode 100644 queue-6.1/drm-bridge-panel-fix-runtime-warning-on-panel-bridge.patch create mode 100644 queue-6.1/drm-komeda-check-for-error-valued-pointer.patch create mode 100644 queue-6.1/drm-vmwgfx-3d-disabled-should-not-effect-stdu-memory.patch create mode 100644 queue-6.1/drm-vmwgfx-filter-modes-which-exceed-graphics-memory.patch create mode 100644 queue-6.1/drm-vmwgfx-port-the-framebuffer-code-to-drm-fb-helpe.patch create mode 100644 queue-6.1/drm-vmwgfx-refactor-drm-connector-probing-for-displa.patch create mode 100644 queue-6.1/drm-vmwgfx-remove-stdu-logic-from-generic-mode_valid.patch create mode 100644 queue-6.1/geneve-fix-incorrect-inner-network-header-offset-whe.patch create mode 100644 queue-6.1/gpio-tqmx86-convert-to-immutable-irq_chip.patch create mode 100644 queue-6.1/gpio-tqmx86-fix-broken-irq_type_edge_both-interrupt-.patch create mode 100644 queue-6.1/gpio-tqmx86-fix-typo-in-kconfig-label.patch create mode 100644 queue-6.1/gpio-tqmx86-introduce-shadow-register-for-gpio-outpu.patch create mode 100644 queue-6.1/gpio-tqmx86-remove-unneeded-call-to-platform_set_drv.patch create mode 100644 queue-6.1/gpio-tqmx86-store-irq-trigger-type-and-unmask-status.patch create mode 100644 queue-6.1/gve-ignore-nonrelevant-gso-type-bits-when-processing.patch create mode 100644 queue-6.1/hid-core-remove-unnecessary-warn_on-in-implement.patch create mode 100644 queue-6.1/hid-logitech-dj-fix-memory-leak-in-logi_dj_recv_swit.patch create mode 100644 queue-6.1/iommu-amd-fix-sysfs-leak-in-iommu-init.patch create mode 100644 queue-6.1/ionic-fix-use-after-netif_napi_del.patch create mode 100644 queue-6.1/liquidio-adjust-a-null-pointer-handling-path-in-lio_.patch create mode 100644 queue-6.1/net-bridge-mst-fix-suspicious-rcu-usage-in-br_mst_se.patch create mode 100644 queue-6.1/net-bridge-mst-pass-vlan-group-directly-to-br_mst_vl.patch create mode 100644 queue-6.1/net-hns3-add-cond_resched-to-hns3-ring-buffer-init-p.patch create mode 100644 queue-6.1/net-hns3-fix-kernel-crash-problem-in-concurrent-scen.patch create mode 100644 queue-6.1/net-ipv6-fix-the-rt-cache-flush-via-sysctl-using-a-p.patch create mode 100644 queue-6.1/net-mlx5e-fix-features-validation-check-for-tunneled.patch create mode 100644 queue-6.1/net-pse-pd-use-eopnotsupp-error-code-instead-of-enot.patch create mode 100644 queue-6.1/net-sfp-always-call-sfp_sm_mod_remove-on-remove.patch create mode 100644 queue-6.1/net-stmmac-replace-priv-speed-with-the-porttransmitr.patch create mode 100644 queue-6.1/netfilter-ipset-fix-race-between-namespace-cleanup-a.patch create mode 100644 queue-6.1/netfilter-use-flowlabel-flow-key-when-re-routing-man.patch create mode 100644 queue-6.1/nfs-add-barriers-when-testing-for-nfs_fsdata_blocked.patch create mode 100644 queue-6.1/nfsv4.1-enforce-rootpath-check-in-fs_location-query.patch create mode 100644 queue-6.1/nvmet-passthru-propagate-status-from-id-override-fun.patch create mode 100644 queue-6.1/platform-x86-dell-smbios-fix-wrong-token-data-in-sys.patch create mode 100644 queue-6.1/selftests-ftrace-fix-to-check-required-event-file.patch create mode 100644 queue-6.1/sunrpc-return-proper-error-from-gss_wrap_req_priv.patch create mode 100644 queue-6.1/tcp-fix-race-in-tcp_v6_syn_recv_sock.patch diff --git a/queue-6.1/af_unix-read-with-msg_peek-loops-if-the-first-unread.patch b/queue-6.1/af_unix-read-with-msg_peek-loops-if-the-first-unread.patch new file mode 100644 index 00000000000..fad0ef2c738 --- /dev/null +++ b/queue-6.1/af_unix-read-with-msg_peek-loops-if-the-first-unread.patch @@ -0,0 +1,85 @@ +From 645ccb61deb253d70b672e41de5c336946de151d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Jun 2024 01:46:39 -0700 +Subject: af_unix: Read with MSG_PEEK loops if the first unread byte is OOB + +From: Rao Shoaib + +[ Upstream commit a6736a0addd60fccc3a3508461d72314cc609772 ] + +Read with MSG_PEEK flag loops if the first byte to read is an OOB byte. +commit 22dd70eb2c3d ("af_unix: Don't peek OOB data without MSG_OOB.") +addresses the loop issue but does not address the issue that no data +beyond OOB byte can be read. + +>>> from socket import * +>>> c1, c2 = socketpair(AF_UNIX, SOCK_STREAM) +>>> c1.send(b'a', MSG_OOB) +1 +>>> c1.send(b'b') +1 +>>> c2.recv(1, MSG_PEEK | MSG_DONTWAIT) +b'b' + +>>> from socket import * +>>> c1, c2 = socketpair(AF_UNIX, SOCK_STREAM) +>>> c2.setsockopt(SOL_SOCKET, SO_OOBINLINE, 1) +>>> c1.send(b'a', MSG_OOB) +1 +>>> c1.send(b'b') +1 +>>> c2.recv(1, MSG_PEEK | MSG_DONTWAIT) +b'a' +>>> c2.recv(1, MSG_PEEK | MSG_DONTWAIT) +b'a' +>>> c2.recv(1, MSG_DONTWAIT) +b'a' +>>> c2.recv(1, MSG_PEEK | MSG_DONTWAIT) +b'b' +>>> + +Fixes: 314001f0bf92 ("af_unix: Add OOB support") +Signed-off-by: Rao Shoaib +Reviewed-by: Kuniyuki Iwashima +Link: https://lore.kernel.org/r/20240611084639.2248934-1-Rao.Shoaib@oracle.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/unix/af_unix.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index bb94a67229aa3..3905cdcaa5184 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -2682,18 +2682,18 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, + if (skb == u->oob_skb) { + if (copied) { + skb = NULL; +- } else if (sock_flag(sk, SOCK_URGINLINE)) { +- if (!(flags & MSG_PEEK)) { ++ } else if (!(flags & MSG_PEEK)) { ++ if (sock_flag(sk, SOCK_URGINLINE)) { + WRITE_ONCE(u->oob_skb, NULL); + consume_skb(skb); ++ } else { ++ __skb_unlink(skb, &sk->sk_receive_queue); ++ WRITE_ONCE(u->oob_skb, NULL); ++ unlinked_skb = skb; ++ skb = skb_peek(&sk->sk_receive_queue); + } +- } else if (flags & MSG_PEEK) { +- skb = NULL; +- } else { +- __skb_unlink(skb, &sk->sk_receive_queue); +- WRITE_ONCE(u->oob_skb, NULL); +- unlinked_skb = skb; +- skb = skb_peek(&sk->sk_receive_queue); ++ } else if (!sock_flag(sk, SOCK_URGINLINE)) { ++ skb = skb_peek_next(skb, &sk->sk_receive_queue); + } + } + +-- +2.43.0 + diff --git a/queue-6.1/bluetooth-l2cap-fix-rejecting-l2cap_conn_param_updat.patch b/queue-6.1/bluetooth-l2cap-fix-rejecting-l2cap_conn_param_updat.patch new file mode 100644 index 00000000000..8ad6762375b --- /dev/null +++ b/queue-6.1/bluetooth-l2cap-fix-rejecting-l2cap_conn_param_updat.patch @@ -0,0 +1,108 @@ +From 7ad55ecff9b7b4c98082b48e6fdcaa76c099ff41 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 May 2024 16:03:07 -0400 +Subject: Bluetooth: L2CAP: Fix rejecting L2CAP_CONN_PARAM_UPDATE_REQ + +From: Luiz Augusto von Dentz + +[ Upstream commit 806a5198c05987b748b50f3d0c0cfb3d417381a4 ] + +This removes the bogus check for max > hcon->le_conn_max_interval since +the later is just the initial maximum conn interval not the maximum the +stack could support which is really 3200=4000ms. + +In order to pass GAP/CONN/CPUP/BV-05-C one shall probably enter values +of the following fields in IXIT that would cause hci_check_conn_params +to fail: + +TSPX_conn_update_int_min +TSPX_conn_update_int_max +TSPX_conn_update_peripheral_latency +TSPX_conn_update_supervision_timeout + +Link: https://github.com/bluez/bluez/issues/847 +Fixes: e4b019515f95 ("Bluetooth: Enforce validation on max value of connection interval") +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + include/net/bluetooth/hci_core.h | 36 ++++++++++++++++++++++++++++---- + net/bluetooth/l2cap_core.c | 8 +------ + 2 files changed, 33 insertions(+), 11 deletions(-) + +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index c50a41f1782a4..9df7e29386bcc 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -1936,18 +1936,46 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, + { + u16 max_latency; + +- if (min > max || min < 6 || max > 3200) ++ if (min > max) { ++ BT_WARN("min %d > max %d", min, max); + return -EINVAL; ++ } ++ ++ if (min < 6) { ++ BT_WARN("min %d < 6", min); ++ return -EINVAL; ++ } ++ ++ if (max > 3200) { ++ BT_WARN("max %d > 3200", max); ++ return -EINVAL; ++ } ++ ++ if (to_multiplier < 10) { ++ BT_WARN("to_multiplier %d < 10", to_multiplier); ++ return -EINVAL; ++ } + +- if (to_multiplier < 10 || to_multiplier > 3200) ++ if (to_multiplier > 3200) { ++ BT_WARN("to_multiplier %d > 3200", to_multiplier); + return -EINVAL; ++ } + +- if (max >= to_multiplier * 8) ++ if (max >= to_multiplier * 8) { ++ BT_WARN("max %d >= to_multiplier %d * 8", max, to_multiplier); + return -EINVAL; ++ } + + max_latency = (to_multiplier * 4 / max) - 1; +- if (latency > 499 || latency > max_latency) ++ if (latency > 499) { ++ BT_WARN("latency %d > 499", latency); + return -EINVAL; ++ } ++ ++ if (latency > max_latency) { ++ BT_WARN("latency %d > max_latency %d", latency, max_latency); ++ return -EINVAL; ++ } + + return 0; + } +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 5f9a599baa34d..a204488a21759 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -5641,13 +5641,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, + + memset(&rsp, 0, sizeof(rsp)); + +- if (max > hcon->le_conn_max_interval) { +- BT_DBG("requested connection interval exceeds current bounds."); +- err = -EINVAL; +- } else { +- err = hci_check_conn_params(min, max, latency, to_multiplier); +- } +- ++ err = hci_check_conn_params(min, max, latency, to_multiplier); + if (err) + rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); + else +-- +2.43.0 + diff --git a/queue-6.1/bnxt_en-adjust-logging-of-firmware-messages-in-case-.patch b/queue-6.1/bnxt_en-adjust-logging-of-firmware-messages-in-case-.patch new file mode 100644 index 00000000000..8567f0ae59b --- /dev/null +++ b/queue-6.1/bnxt_en-adjust-logging-of-firmware-messages-in-case-.patch @@ -0,0 +1,47 @@ +From 2b1acec911c8ec1d2c63842b353c633d15331f96 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Jun 2024 11:25:46 +0300 +Subject: bnxt_en: Adjust logging of firmware messages in case of released + token in __hwrm_send() + +From: Aleksandr Mishin + +[ Upstream commit a9b9741854a9fe9df948af49ca5514e0ed0429df ] + +In case of token is released due to token->state == BNXT_HWRM_DEFERRED, +released token (set to NULL) is used in log messages. This issue is +expected to be prevented by HWRM_ERR_CODE_PF_UNAVAILABLE error code. But +this error code is returned by recent firmware. So some firmware may not +return it. This may lead to NULL pointer dereference. +Adjust this issue by adding token pointer check. + +Found by Linux Verification Center (linuxtesting.org) with SVACE. + +Fixes: 8fa4219dba8e ("bnxt_en: add dynamic debug support for HWRM messages") +Suggested-by: Michael Chan +Signed-off-by: Aleksandr Mishin +Reviewed-by: Wojciech Drewek +Reviewed-by: Michael Chan +Link: https://lore.kernel.org/r/20240611082547.12178-1-amishin@t-argos.ru +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c +index 132442f16fe67..7a4e08b5a8c1b 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c +@@ -678,7 +678,7 @@ static int __hwrm_send(struct bnxt *bp, struct bnxt_hwrm_ctx *ctx) + req_type); + else if (rc && rc != HWRM_ERR_CODE_PF_UNAVAILABLE) + hwrm_err(bp, ctx, "hwrm req_type 0x%x seq id 0x%x error 0x%x\n", +- req_type, token->seq_id, rc); ++ req_type, le16_to_cpu(ctx->req->seq_id), rc); + rc = __hwrm_to_stderr(rc); + exit: + if (token) +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-add-output-string-to-cachefiles_obj_-get-.patch b/queue-6.1/cachefiles-add-output-string-to-cachefiles_obj_-get-.patch new file mode 100644 index 00000000000..f235a4e5393 --- /dev/null +++ b/queue-6.1/cachefiles-add-output-string-to-cachefiles_obj_-get-.patch @@ -0,0 +1,40 @@ +From 21f13c1c048f6d50789498512f5280f70d6e5e41 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:42:57 +0800 +Subject: cachefiles: add output string to cachefiles_obj_[get|put]_ondemand_fd + +From: Baokun Li + +[ Upstream commit cc5ac966f26193ab185cc43d64d9f1ae998ccb6e ] + +This lets us see the correct trace output. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-2-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Reviewed-by: Jingbo Xu +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + include/trace/events/cachefiles.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cachefiles.h +index d8d4d73fe7b6a..b4939097f5116 100644 +--- a/include/trace/events/cachefiles.h ++++ b/include/trace/events/cachefiles.h +@@ -127,7 +127,9 @@ enum cachefiles_error_trace { + EM(cachefiles_obj_see_lookup_cookie, "SEE lookup_cookie") \ + EM(cachefiles_obj_see_lookup_failed, "SEE lookup_failed") \ + EM(cachefiles_obj_see_withdraw_cookie, "SEE withdraw_cookie") \ +- E_(cachefiles_obj_see_withdrawal, "SEE withdrawal") ++ EM(cachefiles_obj_see_withdrawal, "SEE withdrawal") \ ++ EM(cachefiles_obj_get_ondemand_fd, "GET ondemand_fd") \ ++ E_(cachefiles_obj_put_ondemand_fd, "PUT ondemand_fd") + + #define cachefiles_coherency_traces \ + EM(cachefiles_coherency_check_aux, "BAD aux ") \ +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-add-restore-command-to-recover-inflight-o.patch b/queue-6.1/cachefiles-add-restore-command-to-recover-inflight-o.patch new file mode 100644 index 00000000000..bb849285243 --- /dev/null +++ b/queue-6.1/cachefiles-add-restore-command-to-recover-inflight-o.patch @@ -0,0 +1,94 @@ +From b6f72f3b9933d594808a7923eb2c8f1f040954dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Nov 2023 12:14:22 +0800 +Subject: cachefiles: add restore command to recover inflight ondemand read + requests + +From: Jia Zhu + +[ Upstream commit e73fa11a356ca0905c3cc648eaacc6f0f2d2c8b3 ] + +Previously, in ondemand read scenario, if the anonymous fd was closed by +user daemon, inflight and subsequent read requests would return EIO. +As long as the device connection is not released, user daemon can hold +and restore inflight requests by setting the request flag to +CACHEFILES_REQ_NEW. + +Suggested-by: Gao Xiang +Signed-off-by: Jia Zhu +Signed-off-by: Xin Yin +Link: https://lore.kernel.org/r/20231120041422.75170-6-zhujia.zj@bytedance.com +Reviewed-by: Jingbo Xu +Reviewed-by: David Howells +Signed-off-by: Christian Brauner +Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/daemon.c | 1 + + fs/cachefiles/internal.h | 3 +++ + fs/cachefiles/ondemand.c | 23 +++++++++++++++++++++++ + 3 files changed, 27 insertions(+) + +diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c +index 7d1f456e376dd..26b487e112596 100644 +--- a/fs/cachefiles/daemon.c ++++ b/fs/cachefiles/daemon.c +@@ -77,6 +77,7 @@ static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] = { + { "tag", cachefiles_daemon_tag }, + #ifdef CONFIG_CACHEFILES_ONDEMAND + { "copen", cachefiles_ondemand_copen }, ++ { "restore", cachefiles_ondemand_restore }, + #endif + { "", NULL } + }; +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index 33fe418aca770..361356d0e866a 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -304,6 +304,9 @@ extern ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + extern int cachefiles_ondemand_copen(struct cachefiles_cache *cache, + char *args); + ++extern int cachefiles_ondemand_restore(struct cachefiles_cache *cache, ++ char *args); ++ + extern int cachefiles_ondemand_init_object(struct cachefiles_object *object); + extern void cachefiles_ondemand_clean_object(struct cachefiles_object *object); + +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 8118649d30727..6d8f7f01a73ac 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -214,6 +214,29 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + return ret; + } + ++int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args) ++{ ++ struct cachefiles_req *req; ++ ++ XA_STATE(xas, &cache->reqs, 0); ++ ++ if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags)) ++ return -EOPNOTSUPP; ++ ++ /* ++ * Reset the requests to CACHEFILES_REQ_NEW state, so that the ++ * requests have been processed halfway before the crash of the ++ * user daemon could be reprocessed after the recovery. ++ */ ++ xas_lock(&xas); ++ xas_for_each(&xas, req, ULONG_MAX) ++ xas_set_mark(&xas, CACHEFILES_REQ_NEW); ++ xas_unlock(&xas); ++ ++ wake_up_all(&cache->daemon_pollwq); ++ return 0; ++} ++ + static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + { + struct cachefiles_object *object; +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-add-spin_lock-for-cachefiles_ondemand_inf.patch b/queue-6.1/cachefiles-add-spin_lock-for-cachefiles_ondemand_inf.patch new file mode 100644 index 00000000000..f74fa5d063b --- /dev/null +++ b/queue-6.1/cachefiles-add-spin_lock-for-cachefiles_ondemand_inf.patch @@ -0,0 +1,138 @@ +From 6e3d5571cc765e98e737c13858d48fe33f37bfd4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:03 +0800 +Subject: cachefiles: add spin_lock for cachefiles_ondemand_info + +From: Baokun Li + +[ Upstream commit 0a790040838c736495d5afd6b2d636f159f817f1 ] + +The following concurrency may cause a read request to fail to be completed +and result in a hung: + + t1 | t2 +--------------------------------------------------------- + cachefiles_ondemand_copen + req = xa_erase(&cache->reqs, id) +// Anon fd is maliciously closed. +cachefiles_ondemand_fd_release + xa_lock(&cache->reqs) + cachefiles_ondemand_set_object_close(object) + xa_unlock(&cache->reqs) + cachefiles_ondemand_set_object_open + // No one will ever close it again. +cachefiles_ondemand_daemon_read + cachefiles_ondemand_select_req + // Get a read req but its fd is already closed. + // The daemon can't issue a cread ioctl with an closed fd, then hung. + +So add spin_lock for cachefiles_ondemand_info to protect ondemand_id and +state, thus we can avoid the above problem in cachefiles_ondemand_copen() +by using ondemand_id to determine if fd has been closed. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-8-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/cachefiles/internal.h | 1 + + fs/cachefiles/ondemand.c | 35 ++++++++++++++++++++++++++++++++++- + 2 files changed, 35 insertions(+), 1 deletion(-) + +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index b9a90f1a0c015..33fe418aca770 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -55,6 +55,7 @@ struct cachefiles_ondemand_info { + int ondemand_id; + enum cachefiles_object_state state; + struct cachefiles_object *object; ++ spinlock_t lock; + }; + + /* +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 8e130de952f7d..8118649d30727 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -10,13 +10,16 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, + struct cachefiles_object *object = file->private_data; + struct cachefiles_cache *cache = object->volume->cache; + struct cachefiles_ondemand_info *info = object->ondemand; +- int object_id = info->ondemand_id; ++ int object_id; + struct cachefiles_req *req; + XA_STATE(xas, &cache->reqs, 0); + + xa_lock(&cache->reqs); ++ spin_lock(&info->lock); ++ object_id = info->ondemand_id; + info->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED; + cachefiles_ondemand_set_object_close(object); ++ spin_unlock(&info->lock); + + /* Only flush CACHEFILES_REQ_NEW marked req to avoid race with daemon_read */ + xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) { +@@ -116,6 +119,7 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + { + struct cachefiles_req *req; + struct fscache_cookie *cookie; ++ struct cachefiles_ondemand_info *info; + char *pid, *psize; + unsigned long id; + long size; +@@ -166,6 +170,33 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + goto out; + } + ++ info = req->object->ondemand; ++ spin_lock(&info->lock); ++ /* ++ * The anonymous fd was closed before copen ? Fail the request. ++ * ++ * t1 | t2 ++ * --------------------------------------------------------- ++ * cachefiles_ondemand_copen ++ * req = xa_erase(&cache->reqs, id) ++ * // Anon fd is maliciously closed. ++ * cachefiles_ondemand_fd_release ++ * xa_lock(&cache->reqs) ++ * cachefiles_ondemand_set_object_close(object) ++ * xa_unlock(&cache->reqs) ++ * cachefiles_ondemand_set_object_open ++ * // No one will ever close it again. ++ * cachefiles_ondemand_daemon_read ++ * cachefiles_ondemand_select_req ++ * ++ * Get a read req but its fd is already closed. The daemon can't ++ * issue a cread ioctl with an closed fd, then hung. ++ */ ++ if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED) { ++ spin_unlock(&info->lock); ++ req->error = -EBADFD; ++ goto out; ++ } + cookie = req->object->cookie; + cookie->object_size = size; + if (size) +@@ -175,6 +206,7 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + trace_cachefiles_ondemand_copen(req->object, id, size); + + cachefiles_ondemand_set_object_open(req->object); ++ spin_unlock(&info->lock); + wake_up_all(&cache->daemon_pollwq); + + out: +@@ -552,6 +584,7 @@ int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object, + return -ENOMEM; + + object->ondemand->object = object; ++ spin_lock_init(&object->ondemand->lock); + INIT_WORK(&object->ondemand->ondemand_work, ondemand_object_worker); + return 0; + } +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-defer-exposing-anon_fd-until-after-copy_t.patch b/queue-6.1/cachefiles-defer-exposing-anon_fd-until-after-copy_t.patch new file mode 100644 index 00000000000..f9a4185ee00 --- /dev/null +++ b/queue-6.1/cachefiles-defer-exposing-anon_fd-until-after-copy_t.patch @@ -0,0 +1,164 @@ +From 29d16a170159a19259e8c6dfa8bec1fbf40e55d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:05 +0800 +Subject: cachefiles: defer exposing anon_fd until after copy_to_user() + succeeds + +From: Baokun Li + +[ Upstream commit 4b4391e77a6bf24cba2ef1590e113d9b73b11039 ] + +After installing the anonymous fd, we can now see it in userland and close +it. However, at this point we may not have gotten the reference count of +the cache, but we will put it during colse fd, so this may cause a cache +UAF. + +So grab the cache reference count before fd_install(). In addition, by +kernel convention, fd is taken over by the user land after fd_install(), +and the kernel should not call close_fd() after that, i.e., it should call +fd_install() after everything is ready, thus fd_install() is called after +copy_to_user() succeeds. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Suggested-by: Hou Tao +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-10-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/cachefiles/ondemand.c | 53 +++++++++++++++++++++++++--------------- + 1 file changed, 33 insertions(+), 20 deletions(-) + +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 773c3b407a33b..a8cfa5047aaf8 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -4,6 +4,11 @@ + #include + #include "internal.h" + ++struct ondemand_anon_file { ++ struct file *file; ++ int fd; ++}; ++ + static inline void cachefiles_req_put(struct cachefiles_req *req) + { + if (refcount_dec_and_test(&req->ref)) +@@ -250,14 +255,14 @@ int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args) + return 0; + } + +-static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) ++static int cachefiles_ondemand_get_fd(struct cachefiles_req *req, ++ struct ondemand_anon_file *anon_file) + { + struct cachefiles_object *object; + struct cachefiles_cache *cache; + struct cachefiles_open *load; +- struct file *file; + u32 object_id; +- int ret, fd; ++ int ret; + + object = cachefiles_grab_object(req->object, + cachefiles_obj_get_ondemand_fd); +@@ -269,16 +274,16 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + if (ret < 0) + goto err; + +- fd = get_unused_fd_flags(O_WRONLY); +- if (fd < 0) { +- ret = fd; ++ anon_file->fd = get_unused_fd_flags(O_WRONLY); ++ if (anon_file->fd < 0) { ++ ret = anon_file->fd; + goto err_free_id; + } + +- file = anon_inode_getfile("[cachefiles]", &cachefiles_ondemand_fd_fops, +- object, O_WRONLY); +- if (IS_ERR(file)) { +- ret = PTR_ERR(file); ++ anon_file->file = anon_inode_getfile("[cachefiles]", ++ &cachefiles_ondemand_fd_fops, object, O_WRONLY); ++ if (IS_ERR(anon_file->file)) { ++ ret = PTR_ERR(anon_file->file); + goto err_put_fd; + } + +@@ -286,16 +291,15 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + if (object->ondemand->ondemand_id > 0) { + spin_unlock(&object->ondemand->lock); + /* Pair with check in cachefiles_ondemand_fd_release(). */ +- file->private_data = NULL; ++ anon_file->file->private_data = NULL; + ret = -EEXIST; + goto err_put_file; + } + +- file->f_mode |= FMODE_PWRITE | FMODE_LSEEK; +- fd_install(fd, file); ++ anon_file->file->f_mode |= FMODE_PWRITE | FMODE_LSEEK; + + load = (void *)req->msg.data; +- load->fd = fd; ++ load->fd = anon_file->fd; + object->ondemand->ondemand_id = object_id; + spin_unlock(&object->ondemand->lock); + +@@ -304,9 +308,11 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + return 0; + + err_put_file: +- fput(file); ++ fput(anon_file->file); ++ anon_file->file = NULL; + err_put_fd: +- put_unused_fd(fd); ++ put_unused_fd(anon_file->fd); ++ anon_file->fd = ret; + err_free_id: + xa_erase(&cache->ondemand_ids, object_id); + err: +@@ -363,6 +369,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + struct cachefiles_msg *msg; + size_t n; + int ret = 0; ++ struct ondemand_anon_file anon_file; + XA_STATE(xas, &cache->reqs, cache->req_id_next); + + xa_lock(&cache->reqs); +@@ -396,7 +403,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + xa_unlock(&cache->reqs); + + if (msg->opcode == CACHEFILES_OP_OPEN) { +- ret = cachefiles_ondemand_get_fd(req); ++ ret = cachefiles_ondemand_get_fd(req, &anon_file); + if (ret) + goto out; + } +@@ -404,10 +411,16 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + msg->msg_id = xas.xa_index; + msg->object_id = req->object->ondemand->ondemand_id; + +- if (copy_to_user(_buffer, msg, n) != 0) { ++ if (copy_to_user(_buffer, msg, n) != 0) + ret = -EFAULT; +- if (msg->opcode == CACHEFILES_OP_OPEN) +- close_fd(((struct cachefiles_open *)msg->data)->fd); ++ ++ if (msg->opcode == CACHEFILES_OP_OPEN) { ++ if (ret < 0) { ++ fput(anon_file.file); ++ put_unused_fd(anon_file.fd); ++ goto out; ++ } ++ fd_install(anon_file.fd, anon_file.file); + } + out: + cachefiles_put_object(req->object, cachefiles_obj_put_read_req); +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-extract-ondemand-info-field-from-cachefil.patch b/queue-6.1/cachefiles-extract-ondemand-info-field-from-cachefil.patch new file mode 100644 index 00000000000..2df3dba3212 --- /dev/null +++ b/queue-6.1/cachefiles-extract-ondemand-info-field-from-cachefil.patch @@ -0,0 +1,206 @@ +From 446de368ae6891cbc249582539c7cc47f80378ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Nov 2023 12:14:19 +0800 +Subject: cachefiles: extract ondemand info field from cachefiles_object + +From: Jia Zhu + +[ Upstream commit 3c5ecfe16e7699011c12c2d44e55437415331fa3 ] + +We'll introduce a @work_struct field for @object in subsequent patches, +it will enlarge the size of @object. +As the result of that, this commit extracts ondemand info field from +@object. + +Signed-off-by: Jia Zhu +Link: https://lore.kernel.org/r/20231120041422.75170-3-zhujia.zj@bytedance.com +Reviewed-by: Jingbo Xu +Reviewed-by: David Howells +Signed-off-by: Christian Brauner +Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/interface.c | 7 ++++++- + fs/cachefiles/internal.h | 26 ++++++++++++++++++++++---- + fs/cachefiles/ondemand.c | 34 ++++++++++++++++++++++++++++------ + 3 files changed, 56 insertions(+), 11 deletions(-) + +diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c +index a69073a1d3f06..bde23e156a63c 100644 +--- a/fs/cachefiles/interface.c ++++ b/fs/cachefiles/interface.c +@@ -31,6 +31,11 @@ struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *cookie) + if (!object) + return NULL; + ++ if (cachefiles_ondemand_init_obj_info(object, volume)) { ++ kmem_cache_free(cachefiles_object_jar, object); ++ return NULL; ++ } ++ + refcount_set(&object->ref, 1); + + spin_lock_init(&object->lock); +@@ -88,7 +93,7 @@ void cachefiles_put_object(struct cachefiles_object *object, + ASSERTCMP(object->file, ==, NULL); + + kfree(object->d_name); +- ++ cachefiles_ondemand_deinit_obj_info(object); + cache = object->volume->cache->cache; + fscache_put_cookie(object->cookie, fscache_cookie_put_object); + object->cookie = NULL; +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index 00beedeaec183..b0fe76964bc0d 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -49,6 +49,12 @@ enum cachefiles_object_state { + CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */ + }; + ++struct cachefiles_ondemand_info { ++ int ondemand_id; ++ enum cachefiles_object_state state; ++ struct cachefiles_object *object; ++}; ++ + /* + * Backing file state. + */ +@@ -66,8 +72,7 @@ struct cachefiles_object { + unsigned long flags; + #define CACHEFILES_OBJECT_USING_TMPFILE 0 /* Have an unlinked tmpfile */ + #ifdef CONFIG_CACHEFILES_ONDEMAND +- int ondemand_id; +- enum cachefiles_object_state state; ++ struct cachefiles_ondemand_info *ondemand; + #endif + }; + +@@ -302,17 +307,21 @@ extern void cachefiles_ondemand_clean_object(struct cachefiles_object *object); + extern int cachefiles_ondemand_read(struct cachefiles_object *object, + loff_t pos, size_t len); + ++extern int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj, ++ struct cachefiles_volume *volume); ++extern void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj); ++ + #define CACHEFILES_OBJECT_STATE_FUNCS(_state, _STATE) \ + static inline bool \ + cachefiles_ondemand_object_is_##_state(const struct cachefiles_object *object) \ + { \ +- return object->state == CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ ++ return object->ondemand->state == CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ + } \ + \ + static inline void \ + cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \ + { \ +- object->state = CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ ++ object->ondemand->state = CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ + } + + CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN); +@@ -338,6 +347,15 @@ static inline int cachefiles_ondemand_read(struct cachefiles_object *object, + { + return -EOPNOTSUPP; + } ++ ++static inline int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj, ++ struct cachefiles_volume *volume) ++{ ++ return 0; ++} ++static inline void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj) ++{ ++} + #endif + + /* +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 90456b8a4b3e0..deb7e3007aa1d 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -9,12 +9,13 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, + { + struct cachefiles_object *object = file->private_data; + struct cachefiles_cache *cache = object->volume->cache; +- int object_id = object->ondemand_id; ++ struct cachefiles_ondemand_info *info = object->ondemand; ++ int object_id = info->ondemand_id; + struct cachefiles_req *req; + XA_STATE(xas, &cache->reqs, 0); + + xa_lock(&cache->reqs); +- object->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED; ++ info->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED; + cachefiles_ondemand_set_object_close(object); + + /* +@@ -222,7 +223,7 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + load = (void *)req->msg.data; + load->fd = fd; + req->msg.object_id = object_id; +- object->ondemand_id = object_id; ++ object->ondemand->ondemand_id = object_id; + + cachefiles_get_unbind_pincount(cache); + trace_cachefiles_ondemand_open(object, &req->msg, load); +@@ -368,7 +369,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + + if (opcode != CACHEFILES_OP_OPEN && + !cachefiles_ondemand_object_is_open(object)) { +- WARN_ON_ONCE(object->ondemand_id == 0); ++ WARN_ON_ONCE(object->ondemand->ondemand_id == 0); + xas_unlock(&xas); + ret = -EIO; + goto out; +@@ -438,7 +439,7 @@ static int cachefiles_ondemand_init_close_req(struct cachefiles_req *req, + if (!cachefiles_ondemand_object_is_open(object)) + return -ENOENT; + +- req->msg.object_id = object->ondemand_id; ++ req->msg.object_id = object->ondemand->ondemand_id; + trace_cachefiles_ondemand_close(object, &req->msg); + return 0; + } +@@ -454,7 +455,7 @@ static int cachefiles_ondemand_init_read_req(struct cachefiles_req *req, + struct cachefiles_object *object = req->object; + struct cachefiles_read *load = (void *)req->msg.data; + struct cachefiles_read_ctx *read_ctx = private; +- int object_id = object->ondemand_id; ++ int object_id = object->ondemand->ondemand_id; + + /* Stop enqueuing requests when daemon has closed anon_fd. */ + if (!cachefiles_ondemand_object_is_open(object)) { +@@ -500,6 +501,27 @@ void cachefiles_ondemand_clean_object(struct cachefiles_object *object) + cachefiles_ondemand_init_close_req, NULL); + } + ++int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object, ++ struct cachefiles_volume *volume) ++{ ++ if (!cachefiles_in_ondemand_mode(volume->cache)) ++ return 0; ++ ++ object->ondemand = kzalloc(sizeof(struct cachefiles_ondemand_info), ++ GFP_KERNEL); ++ if (!object->ondemand) ++ return -ENOMEM; ++ ++ object->ondemand->object = object; ++ return 0; ++} ++ ++void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *object) ++{ ++ kfree(object->ondemand); ++ object->ondemand = NULL; ++} ++ + int cachefiles_ondemand_read(struct cachefiles_object *object, + loff_t pos, size_t len) + { +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch b/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch new file mode 100644 index 00000000000..80d66828cff --- /dev/null +++ b/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch @@ -0,0 +1,189 @@ +From 8e3ca771693769a98994081151d0695e7b0f05b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:42:59 +0800 +Subject: cachefiles: fix slab-use-after-free in cachefiles_ondemand_get_fd() + +From: Baokun Li + +[ Upstream commit de3e26f9e5b76fc628077578c001c4a51bf54d06 ] + +We got the following issue in a fuzz test of randomly issuing the restore +command: + +================================================================== +BUG: KASAN: slab-use-after-free in cachefiles_ondemand_daemon_read+0x609/0xab0 +Write of size 4 at addr ffff888109164a80 by task ondemand-04-dae/4962 + +CPU: 11 PID: 4962 Comm: ondemand-04-dae Not tainted 6.8.0-rc7-dirty #542 +Call Trace: + kasan_report+0x94/0xc0 + cachefiles_ondemand_daemon_read+0x609/0xab0 + vfs_read+0x169/0xb50 + ksys_read+0xf5/0x1e0 + +Allocated by task 626: + __kmalloc+0x1df/0x4b0 + cachefiles_ondemand_send_req+0x24d/0x690 + cachefiles_create_tmpfile+0x249/0xb30 + cachefiles_create_file+0x6f/0x140 + cachefiles_look_up_object+0x29c/0xa60 + cachefiles_lookup_cookie+0x37d/0xca0 + fscache_cookie_state_machine+0x43c/0x1230 + [...] + +Freed by task 626: + kfree+0xf1/0x2c0 + cachefiles_ondemand_send_req+0x568/0x690 + cachefiles_create_tmpfile+0x249/0xb30 + cachefiles_create_file+0x6f/0x140 + cachefiles_look_up_object+0x29c/0xa60 + cachefiles_lookup_cookie+0x37d/0xca0 + fscache_cookie_state_machine+0x43c/0x1230 + [...] +================================================================== + +Following is the process that triggers the issue: + + mount | daemon_thread1 | daemon_thread2 +------------------------------------------------------------ + cachefiles_ondemand_init_object + cachefiles_ondemand_send_req + REQ_A = kzalloc(sizeof(*req) + data_len) + wait_for_completion(&REQ_A->done) + + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + cachefiles_ondemand_get_fd + copy_to_user(_buffer, msg, n) + process_open_req(REQ_A) + ------ restore ------ + cachefiles_ondemand_restore + xas_for_each(&xas, req, ULONG_MAX) + xas_set_mark(&xas, CACHEFILES_REQ_NEW); + + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + + write(devfd, ("copen %u,%llu", msg->msg_id, size)); + cachefiles_ondemand_copen + xa_erase(&cache->reqs, id) + complete(&REQ_A->done) + kfree(REQ_A) + cachefiles_ondemand_get_fd(REQ_A) + fd = get_unused_fd_flags + file = anon_inode_getfile + fd_install(fd, file) + load = (void *)REQ_A->msg.data; + load->fd = fd; + // load UAF !!! + +This issue is caused by issuing a restore command when the daemon is still +alive, which results in a request being processed multiple times thus +triggering a UAF. So to avoid this problem, add an additional reference +count to cachefiles_req, which is held while waiting and reading, and then +released when the waiting and reading is over. + +Note that since there is only one reference count for waiting, we need to +avoid the same request being completed multiple times, so we can only +complete the request if it is successfully removed from the xarray. + +Fixes: e73fa11a356c ("cachefiles: add restore command to recover inflight ondemand read requests") +Suggested-by: Hou Tao +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-4-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Reviewed-by: Jia Zhu +Reviewed-by: Jingbo Xu +Signed-off-by: Christian Brauner +Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/internal.h | 1 + + fs/cachefiles/ondemand.c | 23 +++++++++++++++++++---- + 2 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index 361356d0e866a..28799c8e2c6f6 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -139,6 +139,7 @@ static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache) + struct cachefiles_req { + struct cachefiles_object *object; + struct completion done; ++ refcount_t ref; + int error; + struct cachefiles_msg msg; + }; +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 6d8f7f01a73ac..f8d0a01795702 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -4,6 +4,12 @@ + #include + #include "internal.h" + ++static inline void cachefiles_req_put(struct cachefiles_req *req) ++{ ++ if (refcount_dec_and_test(&req->ref)) ++ kfree(req); ++} ++ + static int cachefiles_ondemand_fd_release(struct inode *inode, + struct file *file) + { +@@ -362,6 +368,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + + xas_clear_mark(&xas, CACHEFILES_REQ_NEW); + cache->req_id_next = xas.xa_index + 1; ++ refcount_inc(&req->ref); + xa_unlock(&cache->reqs); + + id = xas.xa_index; +@@ -388,15 +395,22 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + complete(&req->done); + } + ++ cachefiles_req_put(req); + return n; + + err_put_fd: + if (msg->opcode == CACHEFILES_OP_OPEN) + close_fd(((struct cachefiles_open *)msg->data)->fd); + error: +- xa_erase(&cache->reqs, id); +- req->error = ret; +- complete(&req->done); ++ xas_reset(&xas); ++ xas_lock(&xas); ++ if (xas_load(&xas) == req) { ++ req->error = ret; ++ complete(&req->done); ++ xas_store(&xas, NULL); ++ } ++ xas_unlock(&xas); ++ cachefiles_req_put(req); + return ret; + } + +@@ -427,6 +441,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + goto out; + } + ++ refcount_set(&req->ref, 1); + req->object = object; + init_completion(&req->done); + req->msg.opcode = opcode; +@@ -488,7 +503,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + wake_up_all(&cache->daemon_pollwq); + wait_for_completion(&req->done); + ret = req->error; +- kfree(req); ++ cachefiles_req_put(req); + return ret; + out: + /* Reset the object to close state in error handling path. +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch-24352 b/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch-24352 new file mode 100644 index 00000000000..19d6f36cdfa --- /dev/null +++ b/queue-6.1/cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch-24352 @@ -0,0 +1,143 @@ +From 209b422248ee51b76d4dab06cba3be99267585f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:00 +0800 +Subject: cachefiles: fix slab-use-after-free in + cachefiles_ondemand_daemon_read() + +From: Baokun Li + +[ Upstream commit da4a827416066191aafeeccee50a8836a826ba10 ] + +We got the following issue in a fuzz test of randomly issuing the restore +command: + +================================================================== +BUG: KASAN: slab-use-after-free in cachefiles_ondemand_daemon_read+0xb41/0xb60 +Read of size 8 at addr ffff888122e84088 by task ondemand-04-dae/963 + +CPU: 13 PID: 963 Comm: ondemand-04-dae Not tainted 6.8.0-dirty #564 +Call Trace: + kasan_report+0x93/0xc0 + cachefiles_ondemand_daemon_read+0xb41/0xb60 + vfs_read+0x169/0xb50 + ksys_read+0xf5/0x1e0 + +Allocated by task 116: + kmem_cache_alloc+0x140/0x3a0 + cachefiles_lookup_cookie+0x140/0xcd0 + fscache_cookie_state_machine+0x43c/0x1230 + [...] + +Freed by task 792: + kmem_cache_free+0xfe/0x390 + cachefiles_put_object+0x241/0x480 + fscache_cookie_state_machine+0x5c8/0x1230 + [...] +================================================================== + +Following is the process that triggers the issue: + + mount | daemon_thread1 | daemon_thread2 +------------------------------------------------------------ +cachefiles_withdraw_cookie + cachefiles_ondemand_clean_object(object) + cachefiles_ondemand_send_req + REQ_A = kzalloc(sizeof(*req) + data_len) + wait_for_completion(&REQ_A->done) + + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + msg->object_id = req->object->ondemand->ondemand_id + ------ restore ------ + cachefiles_ondemand_restore + xas_for_each(&xas, req, ULONG_MAX) + xas_set_mark(&xas, CACHEFILES_REQ_NEW) + + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + copy_to_user(_buffer, msg, n) + xa_erase(&cache->reqs, id) + complete(&REQ_A->done) + ------ close(fd) ------ + cachefiles_ondemand_fd_release + cachefiles_put_object + cachefiles_put_object + kmem_cache_free(cachefiles_object_jar, object) + REQ_A->object->ondemand->ondemand_id + // object UAF !!! + +When we see the request within xa_lock, req->object must not have been +freed yet, so grab the reference count of object before xa_unlock to +avoid the above issue. + +Fixes: 0a7e54c1959c ("cachefiles: resend an open request if the read request's object is closed") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-5-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Reviewed-by: Jia Zhu +Reviewed-by: Jingbo Xu +Signed-off-by: Christian Brauner +Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/ondemand.c | 3 +++ + include/trace/events/cachefiles.h | 6 +++++- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index f8d0a01795702..fd73811c7ce4f 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -369,6 +369,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + xas_clear_mark(&xas, CACHEFILES_REQ_NEW); + cache->req_id_next = xas.xa_index + 1; + refcount_inc(&req->ref); ++ cachefiles_grab_object(req->object, cachefiles_obj_get_read_req); + xa_unlock(&cache->reqs); + + id = xas.xa_index; +@@ -389,6 +390,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + goto err_put_fd; + } + ++ cachefiles_put_object(req->object, cachefiles_obj_put_read_req); + /* CLOSE request has no reply */ + if (msg->opcode == CACHEFILES_OP_CLOSE) { + xa_erase(&cache->reqs, id); +@@ -402,6 +404,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + if (msg->opcode == CACHEFILES_OP_OPEN) + close_fd(((struct cachefiles_open *)msg->data)->fd); + error: ++ cachefiles_put_object(req->object, cachefiles_obj_put_read_req); + xas_reset(&xas); + xas_lock(&xas); + if (xas_load(&xas) == req) { +diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cachefiles.h +index b4939097f5116..dff9a48502247 100644 +--- a/include/trace/events/cachefiles.h ++++ b/include/trace/events/cachefiles.h +@@ -33,6 +33,8 @@ enum cachefiles_obj_ref_trace { + cachefiles_obj_see_withdrawal, + cachefiles_obj_get_ondemand_fd, + cachefiles_obj_put_ondemand_fd, ++ cachefiles_obj_get_read_req, ++ cachefiles_obj_put_read_req, + }; + + enum fscache_why_object_killed { +@@ -129,7 +131,9 @@ enum cachefiles_error_trace { + EM(cachefiles_obj_see_withdraw_cookie, "SEE withdraw_cookie") \ + EM(cachefiles_obj_see_withdrawal, "SEE withdrawal") \ + EM(cachefiles_obj_get_ondemand_fd, "GET ondemand_fd") \ +- E_(cachefiles_obj_put_ondemand_fd, "PUT ondemand_fd") ++ EM(cachefiles_obj_put_ondemand_fd, "PUT ondemand_fd") \ ++ EM(cachefiles_obj_get_read_req, "GET read_req") \ ++ E_(cachefiles_obj_put_read_req, "PUT read_req") + + #define cachefiles_coherency_traces \ + EM(cachefiles_coherency_check_aux, "BAD aux ") \ +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-flush-all-requests-after-setting-cachefil.patch b/queue-6.1/cachefiles-flush-all-requests-after-setting-cachefil.patch new file mode 100644 index 00000000000..6717290c65c --- /dev/null +++ b/queue-6.1/cachefiles-flush-all-requests-after-setting-cachefil.patch @@ -0,0 +1,71 @@ +From 6ce77c57874159044b8cf4757076decb71b70e78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:07 +0800 +Subject: cachefiles: flush all requests after setting CACHEFILES_DEAD + +From: Baokun Li + +[ Upstream commit 85e833cd7243bda7285492b0653c3abb1e2e757b ] + +In ondemand mode, when the daemon is processing an open request, if the +kernel flags the cache as CACHEFILES_DEAD, the cachefiles_daemon_write() +will always return -EIO, so the daemon can't pass the copen to the kernel. +Then the kernel process that is waiting for the copen triggers a hung_task. + +Since the DEAD state is irreversible, it can only be exited by closing +/dev/cachefiles. Therefore, after calling cachefiles_io_error() to mark +the cache as CACHEFILES_DEAD, if in ondemand mode, flush all requests to +avoid the above hungtask. We may still be able to read some of the cached +data before closing the fd of /dev/cachefiles. + +Note that this relies on the patch that adds reference counting to the req, +otherwise it may UAF. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-12-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/cachefiles/daemon.c | 2 +- + fs/cachefiles/internal.h | 3 +++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c +index 26b487e112596..b9945e4f697be 100644 +--- a/fs/cachefiles/daemon.c ++++ b/fs/cachefiles/daemon.c +@@ -133,7 +133,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file) + return 0; + } + +-static void cachefiles_flush_reqs(struct cachefiles_cache *cache) ++void cachefiles_flush_reqs(struct cachefiles_cache *cache) + { + struct xarray *xa = &cache->reqs; + struct cachefiles_req *req; +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index 28799c8e2c6f6..3eea52462fc87 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -188,6 +188,7 @@ extern int cachefiles_has_space(struct cachefiles_cache *cache, + * daemon.c + */ + extern const struct file_operations cachefiles_daemon_fops; ++extern void cachefiles_flush_reqs(struct cachefiles_cache *cache); + extern void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache); + extern void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache); + +@@ -414,6 +415,8 @@ do { \ + pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__); \ + fscache_io_error((___cache)->cache); \ + set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ ++ if (cachefiles_in_ondemand_mode(___cache)) \ ++ cachefiles_flush_reqs(___cache); \ + } while (0) + + #define cachefiles_io_error_obj(object, FMT, ...) \ +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-introduce-object-ondemand-state.patch b/queue-6.1/cachefiles-introduce-object-ondemand-state.patch new file mode 100644 index 00000000000..27a50ba48e1 --- /dev/null +++ b/queue-6.1/cachefiles-introduce-object-ondemand-state.patch @@ -0,0 +1,145 @@ +From 937e1b54b146fba50bf73830e327e0c8c8b3a4ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Nov 2023 12:14:18 +0800 +Subject: cachefiles: introduce object ondemand state + +From: Jia Zhu + +[ Upstream commit 357a18d033143617e9c7d420c8f0dd4cbab5f34d ] + +Previously, @ondemand_id field was used not only to identify ondemand +state of the object, but also to represent the index of the xarray. +This commit introduces @state field to decouple the role of @ondemand_id +and adds helpers to access it. + +Signed-off-by: Jia Zhu +Link: https://lore.kernel.org/r/20231120041422.75170-2-zhujia.zj@bytedance.com +Reviewed-by: Jingbo Xu +Reviewed-by: David Howells +Signed-off-by: Christian Brauner +Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/internal.h | 21 +++++++++++++++++++++ + fs/cachefiles/ondemand.c | 21 +++++++++------------ + 2 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index 2ad58c4652084..00beedeaec183 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -44,6 +44,11 @@ struct cachefiles_volume { + struct dentry *fanout[256]; /* Fanout subdirs */ + }; + ++enum cachefiles_object_state { ++ CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */ ++ CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */ ++}; ++ + /* + * Backing file state. + */ +@@ -62,6 +67,7 @@ struct cachefiles_object { + #define CACHEFILES_OBJECT_USING_TMPFILE 0 /* Have an unlinked tmpfile */ + #ifdef CONFIG_CACHEFILES_ONDEMAND + int ondemand_id; ++ enum cachefiles_object_state state; + #endif + }; + +@@ -296,6 +302,21 @@ extern void cachefiles_ondemand_clean_object(struct cachefiles_object *object); + extern int cachefiles_ondemand_read(struct cachefiles_object *object, + loff_t pos, size_t len); + ++#define CACHEFILES_OBJECT_STATE_FUNCS(_state, _STATE) \ ++static inline bool \ ++cachefiles_ondemand_object_is_##_state(const struct cachefiles_object *object) \ ++{ \ ++ return object->state == CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ ++} \ ++ \ ++static inline void \ ++cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \ ++{ \ ++ object->state = CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \ ++} ++ ++CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN); ++CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE); + #else + static inline ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + char __user *_buffer, size_t buflen) +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 0254ed39f68ce..90456b8a4b3e0 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -15,6 +15,7 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, + + xa_lock(&cache->reqs); + object->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED; ++ cachefiles_ondemand_set_object_close(object); + + /* + * Flush all pending READ requests since their completion depends on +@@ -176,6 +177,8 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); + trace_cachefiles_ondemand_copen(req->object, id, size); + ++ cachefiles_ondemand_set_object_open(req->object); ++ + out: + complete(&req->done); + return ret; +@@ -363,7 +366,8 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + /* coupled with the barrier in cachefiles_flush_reqs() */ + smp_mb(); + +- if (opcode != CACHEFILES_OP_OPEN && object->ondemand_id <= 0) { ++ if (opcode != CACHEFILES_OP_OPEN && ++ !cachefiles_ondemand_object_is_open(object)) { + WARN_ON_ONCE(object->ondemand_id == 0); + xas_unlock(&xas); + ret = -EIO; +@@ -430,18 +434,11 @@ static int cachefiles_ondemand_init_close_req(struct cachefiles_req *req, + void *private) + { + struct cachefiles_object *object = req->object; +- int object_id = object->ondemand_id; + +- /* +- * It's possible that object id is still 0 if the cookie looking up +- * phase failed before OPEN request has ever been sent. Also avoid +- * sending CLOSE request for CACHEFILES_ONDEMAND_ID_CLOSED, which means +- * anon_fd has already been closed. +- */ +- if (object_id <= 0) ++ if (!cachefiles_ondemand_object_is_open(object)) + return -ENOENT; + +- req->msg.object_id = object_id; ++ req->msg.object_id = object->ondemand_id; + trace_cachefiles_ondemand_close(object, &req->msg); + return 0; + } +@@ -460,7 +457,7 @@ static int cachefiles_ondemand_init_read_req(struct cachefiles_req *req, + int object_id = object->ondemand_id; + + /* Stop enqueuing requests when daemon has closed anon_fd. */ +- if (object_id <= 0) { ++ if (!cachefiles_ondemand_object_is_open(object)) { + WARN_ON_ONCE(object_id == 0); + pr_info_once("READ: anonymous fd closed prematurely.\n"); + return -EIO; +@@ -485,7 +482,7 @@ int cachefiles_ondemand_init_object(struct cachefiles_object *object) + * creating a new tmpfile as the cache file. Reuse the previously + * allocated object ID if any. + */ +- if (object->ondemand_id > 0) ++ if (cachefiles_ondemand_object_is_open(object)) + return 0; + + volume_key_size = volume->key[0] + 1; +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch b/queue-6.1/cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch new file mode 100644 index 00000000000..53c2bcdb5dd --- /dev/null +++ b/queue-6.1/cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch @@ -0,0 +1,166 @@ +From 52291051523282e31ebaeb56ac7bb61066a6f1a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:04 +0800 +Subject: cachefiles: never get a new anonymous fd if ondemand_id is valid + +From: Baokun Li + +[ Upstream commit 4988e35e95fc938bdde0e15880fe72042fc86acf ] + +Now every time the daemon reads an open request, it gets a new anonymous fd +and ondemand_id. With the introduction of "restore", it is possible to read +the same open request more than once, and therefore an object can have more +than one anonymous fd. + +If the anonymous fd is not unique, the following concurrencies will result +in an fd leak: + + t1 | t2 | t3 +------------------------------------------------------------ + cachefiles_ondemand_init_object + cachefiles_ondemand_send_req + REQ_A = kzalloc(sizeof(*req) + data_len) + wait_for_completion(&REQ_A->done) + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + cachefiles_ondemand_get_fd + load->fd = fd0 + ondemand_id = object_id0 + ------ restore ------ + cachefiles_ondemand_restore + // restore REQ_A + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + REQ_A = cachefiles_ondemand_select_req + cachefiles_ondemand_get_fd + load->fd = fd1 + ondemand_id = object_id1 + process_open_req(REQ_A) + write(devfd, ("copen %u,%llu", msg->msg_id, size)) + cachefiles_ondemand_copen + xa_erase(&cache->reqs, id) + complete(&REQ_A->done) + kfree(REQ_A) + process_open_req(REQ_A) + // copen fails due to no req + // daemon close(fd1) + cachefiles_ondemand_fd_release + // set object closed + -- umount -- + cachefiles_withdraw_cookie + cachefiles_ondemand_clean_object + cachefiles_ondemand_init_close_req + if (!cachefiles_ondemand_object_is_open(object)) + return -ENOENT; + // The fd0 is not closed until the daemon exits. + +However, the anonymous fd holds the reference count of the object and the +object holds the reference count of the cookie. So even though the cookie +has been relinquished, it will not be unhashed and freed until the daemon +exits. + +In fscache_hash_cookie(), when the same cookie is found in the hash list, +if the cookie is set with the FSCACHE_COOKIE_RELINQUISHED bit, then the new +cookie waits for the old cookie to be unhashed, while the old cookie is +waiting for the leaked fd to be closed, if the daemon does not exit in time +it will trigger a hung task. + +To avoid this, allocate a new anonymous fd only if no anonymous fd has +been allocated (ondemand_id == 0) or if the previously allocated anonymous +fd has been closed (ondemand_id == -1). Moreover, returns an error if +ondemand_id is valid, letting the daemon know that the current userland +restore logic is abnormal and needs to be checked. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-9-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Signed-off-by: Christian Brauner +Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/ondemand.c | 34 ++++++++++++++++++++++++++++------ + 1 file changed, 28 insertions(+), 6 deletions(-) + +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index 99b4bffad4a4f..773c3b407a33b 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -14,11 +14,18 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, + struct file *file) + { + struct cachefiles_object *object = file->private_data; +- struct cachefiles_cache *cache = object->volume->cache; +- struct cachefiles_ondemand_info *info = object->ondemand; ++ struct cachefiles_cache *cache; ++ struct cachefiles_ondemand_info *info; + int object_id; + struct cachefiles_req *req; +- XA_STATE(xas, &cache->reqs, 0); ++ XA_STATE(xas, NULL, 0); ++ ++ if (!object) ++ return 0; ++ ++ info = object->ondemand; ++ cache = object->volume->cache; ++ xas.xa = &cache->reqs; + + xa_lock(&cache->reqs); + spin_lock(&info->lock); +@@ -275,22 +282,39 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + goto err_put_fd; + } + ++ spin_lock(&object->ondemand->lock); ++ if (object->ondemand->ondemand_id > 0) { ++ spin_unlock(&object->ondemand->lock); ++ /* Pair with check in cachefiles_ondemand_fd_release(). */ ++ file->private_data = NULL; ++ ret = -EEXIST; ++ goto err_put_file; ++ } ++ + file->f_mode |= FMODE_PWRITE | FMODE_LSEEK; + fd_install(fd, file); + + load = (void *)req->msg.data; + load->fd = fd; + object->ondemand->ondemand_id = object_id; ++ spin_unlock(&object->ondemand->lock); + + cachefiles_get_unbind_pincount(cache); + trace_cachefiles_ondemand_open(object, &req->msg, load); + return 0; + ++err_put_file: ++ fput(file); + err_put_fd: + put_unused_fd(fd); + err_free_id: + xa_erase(&cache->ondemand_ids, object_id); + err: ++ spin_lock(&object->ondemand->lock); ++ /* Avoid marking an opened object as closed. */ ++ if (object->ondemand->ondemand_id <= 0) ++ cachefiles_ondemand_set_object_close(object); ++ spin_unlock(&object->ondemand->lock); + cachefiles_put_object(object, cachefiles_obj_put_ondemand_fd); + return ret; + } +@@ -373,10 +397,8 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + + if (msg->opcode == CACHEFILES_OP_OPEN) { + ret = cachefiles_ondemand_get_fd(req); +- if (ret) { +- cachefiles_ondemand_set_object_close(req->object); ++ if (ret) + goto out; +- } + } + + msg->msg_id = xas.xa_index; +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-remove-err_put_fd-label-in-cachefiles_ond.patch b/queue-6.1/cachefiles-remove-err_put_fd-label-in-cachefiles_ond.patch new file mode 100644 index 00000000000..8bc416ec42c --- /dev/null +++ b/queue-6.1/cachefiles-remove-err_put_fd-label-in-cachefiles_ond.patch @@ -0,0 +1,108 @@ +From 98eac6377ef752bd500028f35adb7b06d696209b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:43:01 +0800 +Subject: cachefiles: remove err_put_fd label in + cachefiles_ondemand_daemon_read() + +From: Baokun Li + +[ Upstream commit 3e6d704f02aa4c50c7bc5fe91a4401df249a137b ] + +The err_put_fd label is only used once, so remove it to make the code +more readable. In addition, the logic for deleting error request and +CLOSE request is merged to simplify the code. + +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-6-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Reviewed-by: Jia Zhu +Reviewed-by: Gao Xiang +Reviewed-by: Jingbo Xu +Signed-off-by: Christian Brauner +Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/ondemand.c | 45 ++++++++++++++-------------------------- + 1 file changed, 16 insertions(+), 29 deletions(-) + +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index fd73811c7ce4f..99b4bffad4a4f 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -337,7 +337,6 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + { + struct cachefiles_req *req; + struct cachefiles_msg *msg; +- unsigned long id = 0; + size_t n; + int ret = 0; + XA_STATE(xas, &cache->reqs, cache->req_id_next); +@@ -372,49 +371,37 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + cachefiles_grab_object(req->object, cachefiles_obj_get_read_req); + xa_unlock(&cache->reqs); + +- id = xas.xa_index; +- + if (msg->opcode == CACHEFILES_OP_OPEN) { + ret = cachefiles_ondemand_get_fd(req); + if (ret) { + cachefiles_ondemand_set_object_close(req->object); +- goto error; ++ goto out; + } + } + +- msg->msg_id = id; ++ msg->msg_id = xas.xa_index; + msg->object_id = req->object->ondemand->ondemand_id; + + if (copy_to_user(_buffer, msg, n) != 0) { + ret = -EFAULT; +- goto err_put_fd; +- } +- +- cachefiles_put_object(req->object, cachefiles_obj_put_read_req); +- /* CLOSE request has no reply */ +- if (msg->opcode == CACHEFILES_OP_CLOSE) { +- xa_erase(&cache->reqs, id); +- complete(&req->done); ++ if (msg->opcode == CACHEFILES_OP_OPEN) ++ close_fd(((struct cachefiles_open *)msg->data)->fd); + } +- +- cachefiles_req_put(req); +- return n; +- +-err_put_fd: +- if (msg->opcode == CACHEFILES_OP_OPEN) +- close_fd(((struct cachefiles_open *)msg->data)->fd); +-error: ++out: + cachefiles_put_object(req->object, cachefiles_obj_put_read_req); +- xas_reset(&xas); +- xas_lock(&xas); +- if (xas_load(&xas) == req) { +- req->error = ret; +- complete(&req->done); +- xas_store(&xas, NULL); ++ /* Remove error request and CLOSE request has no reply */ ++ if (ret || msg->opcode == CACHEFILES_OP_CLOSE) { ++ xas_reset(&xas); ++ xas_lock(&xas); ++ if (xas_load(&xas) == req) { ++ req->error = ret; ++ complete(&req->done); ++ xas_store(&xas, NULL); ++ } ++ xas_unlock(&xas); + } +- xas_unlock(&xas); + cachefiles_req_put(req); +- return ret; ++ return ret ? ret : n; + } + + typedef int (*init_req_fn)(struct cachefiles_req *req, void *private); +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-remove-requests-from-xarray-during-flushi.patch b/queue-6.1/cachefiles-remove-requests-from-xarray-during-flushi.patch new file mode 100644 index 00000000000..b8e27a39552 --- /dev/null +++ b/queue-6.1/cachefiles-remove-requests-from-xarray-during-flushi.patch @@ -0,0 +1,62 @@ +From 2b56c265a6133d0915651e8c342f7a8b873c0682 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 May 2024 19:42:58 +0800 +Subject: cachefiles: remove requests from xarray during flushing requests + +From: Baokun Li + +[ Upstream commit 0fc75c5940fa634d84e64c93bfc388e1274ed013 ] + +Even with CACHEFILES_DEAD set, we can still read the requests, so in the +following concurrency the request may be used after it has been freed: + + mount | daemon_thread1 | daemon_thread2 +------------------------------------------------------------ + cachefiles_ondemand_init_object + cachefiles_ondemand_send_req + REQ_A = kzalloc(sizeof(*req) + data_len) + wait_for_completion(&REQ_A->done) + cachefiles_daemon_read + cachefiles_ondemand_daemon_read + // close dev fd + cachefiles_flush_reqs + complete(&REQ_A->done) + kfree(REQ_A) + xa_lock(&cache->reqs); + cachefiles_ondemand_select_req + req->msg.opcode != CACHEFILES_OP_READ + // req use-after-free !!! + xa_unlock(&cache->reqs); + xa_destroy(&cache->reqs) + +Hence remove requests from cache->reqs when flushing them to avoid +accessing freed requests. + +Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") +Signed-off-by: Baokun Li +Link: https://lore.kernel.org/r/20240522114308.2402121-3-libaokun@huaweicloud.com +Acked-by: Jeff Layton +Reviewed-by: Jia Zhu +Reviewed-by: Gao Xiang +Reviewed-by: Jingbo Xu +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/cachefiles/daemon.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c +index 5f4df9588620f..7d1f456e376dd 100644 +--- a/fs/cachefiles/daemon.c ++++ b/fs/cachefiles/daemon.c +@@ -158,6 +158,7 @@ static void cachefiles_flush_reqs(struct cachefiles_cache *cache) + xa_for_each(xa, index, req) { + req->error = -EIO; + complete(&req->done); ++ __xa_erase(xa, index); + } + xa_unlock(xa); + +-- +2.43.0 + diff --git a/queue-6.1/cachefiles-resend-an-open-request-if-the-read-reques.patch b/queue-6.1/cachefiles-resend-an-open-request-if-the-read-reques.patch new file mode 100644 index 00000000000..0c7e624d160 --- /dev/null +++ b/queue-6.1/cachefiles-resend-an-open-request-if-the-read-reques.patch @@ -0,0 +1,272 @@ +From 6cb329b030cac9e0ecf361202eb2aa996ad7a7fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Nov 2023 12:14:20 +0800 +Subject: cachefiles: resend an open request if the read request's object is + closed + +From: Jia Zhu + +[ Upstream commit 0a7e54c1959c0feb2de23397ec09c7692364313e ] + +When an anonymous fd is closed by user daemon, if there is a new read +request for this file comes up, the anonymous fd should be re-opened +to handle that read request rather than fail it directly. + +1. Introduce reopening state for objects that are closed but have + inflight/subsequent read requests. +2. No longer flush READ requests but only CLOSE requests when anonymous + fd is closed. +3. Enqueue the reopen work to workqueue, thus user daemon could get rid + of daemon_read context and handle that request smoothly. Otherwise, + the user daemon will send a reopen request and wait for itself to + process the request. + +Signed-off-by: Jia Zhu +Link: https://lore.kernel.org/r/20231120041422.75170-4-zhujia.zj@bytedance.com +Reviewed-by: Jingbo Xu +Reviewed-by: David Howells +Signed-off-by: Christian Brauner +Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info") +Signed-off-by: Sasha Levin +--- + fs/cachefiles/internal.h | 3 ++ + fs/cachefiles/ondemand.c | 98 ++++++++++++++++++++++++++++------------ + 2 files changed, 72 insertions(+), 29 deletions(-) + +diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h +index b0fe76964bc0d..b9a90f1a0c015 100644 +--- a/fs/cachefiles/internal.h ++++ b/fs/cachefiles/internal.h +@@ -47,9 +47,11 @@ struct cachefiles_volume { + enum cachefiles_object_state { + CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */ + CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */ ++ CACHEFILES_ONDEMAND_OBJSTATE_REOPENING, /* Object that was closed and is being reopened. */ + }; + + struct cachefiles_ondemand_info { ++ struct work_struct ondemand_work; + int ondemand_id; + enum cachefiles_object_state state; + struct cachefiles_object *object; +@@ -326,6 +328,7 @@ cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \ + + CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN); + CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE); ++CACHEFILES_OBJECT_STATE_FUNCS(reopening, REOPENING); + #else + static inline ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + char __user *_buffer, size_t buflen) +diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c +index deb7e3007aa1d..8e130de952f7d 100644 +--- a/fs/cachefiles/ondemand.c ++++ b/fs/cachefiles/ondemand.c +@@ -18,14 +18,10 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, + info->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED; + cachefiles_ondemand_set_object_close(object); + +- /* +- * Flush all pending READ requests since their completion depends on +- * anon_fd. +- */ +- xas_for_each(&xas, req, ULONG_MAX) { ++ /* Only flush CACHEFILES_REQ_NEW marked req to avoid race with daemon_read */ ++ xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) { + if (req->msg.object_id == object_id && +- req->msg.opcode == CACHEFILES_OP_READ) { +- req->error = -EIO; ++ req->msg.opcode == CACHEFILES_OP_CLOSE) { + complete(&req->done); + xas_store(&xas, NULL); + } +@@ -179,6 +175,7 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) + trace_cachefiles_ondemand_copen(req->object, id, size); + + cachefiles_ondemand_set_object_open(req->object); ++ wake_up_all(&cache->daemon_pollwq); + + out: + complete(&req->done); +@@ -222,7 +219,6 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + + load = (void *)req->msg.data; + load->fd = fd; +- req->msg.object_id = object_id; + object->ondemand->ondemand_id = object_id; + + cachefiles_get_unbind_pincount(cache); +@@ -238,6 +234,43 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) + return ret; + } + ++static void ondemand_object_worker(struct work_struct *work) ++{ ++ struct cachefiles_ondemand_info *info = ++ container_of(work, struct cachefiles_ondemand_info, ondemand_work); ++ ++ cachefiles_ondemand_init_object(info->object); ++} ++ ++/* ++ * If there are any inflight or subsequent READ requests on the ++ * closed object, reopen it. ++ * Skip read requests whose related object is reopening. ++ */ ++static struct cachefiles_req *cachefiles_ondemand_select_req(struct xa_state *xas, ++ unsigned long xa_max) ++{ ++ struct cachefiles_req *req; ++ struct cachefiles_object *object; ++ struct cachefiles_ondemand_info *info; ++ ++ xas_for_each_marked(xas, req, xa_max, CACHEFILES_REQ_NEW) { ++ if (req->msg.opcode != CACHEFILES_OP_READ) ++ return req; ++ object = req->object; ++ info = object->ondemand; ++ if (cachefiles_ondemand_object_is_close(object)) { ++ cachefiles_ondemand_set_object_reopening(object); ++ queue_work(fscache_wq, &info->ondemand_work); ++ continue; ++ } ++ if (cachefiles_ondemand_object_is_reopening(object)) ++ continue; ++ return req; ++ } ++ return NULL; ++} ++ + ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + char __user *_buffer, size_t buflen) + { +@@ -248,16 +281,16 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + int ret = 0; + XA_STATE(xas, &cache->reqs, cache->req_id_next); + ++ xa_lock(&cache->reqs); + /* + * Cyclically search for a request that has not ever been processed, + * to prevent requests from being processed repeatedly, and make + * request distribution fair. + */ +- xa_lock(&cache->reqs); +- req = xas_find_marked(&xas, UINT_MAX, CACHEFILES_REQ_NEW); ++ req = cachefiles_ondemand_select_req(&xas, ULONG_MAX); + if (!req && cache->req_id_next > 0) { + xas_set(&xas, 0); +- req = xas_find_marked(&xas, cache->req_id_next - 1, CACHEFILES_REQ_NEW); ++ req = cachefiles_ondemand_select_req(&xas, cache->req_id_next - 1); + } + if (!req) { + xa_unlock(&cache->reqs); +@@ -277,14 +310,18 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, + xa_unlock(&cache->reqs); + + id = xas.xa_index; +- msg->msg_id = id; + + if (msg->opcode == CACHEFILES_OP_OPEN) { + ret = cachefiles_ondemand_get_fd(req); +- if (ret) ++ if (ret) { ++ cachefiles_ondemand_set_object_close(req->object); + goto error; ++ } + } + ++ msg->msg_id = id; ++ msg->object_id = req->object->ondemand->ondemand_id; ++ + if (copy_to_user(_buffer, msg, n) != 0) { + ret = -EFAULT; + goto err_put_fd; +@@ -317,19 +354,23 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + void *private) + { + struct cachefiles_cache *cache = object->volume->cache; +- struct cachefiles_req *req; ++ struct cachefiles_req *req = NULL; + XA_STATE(xas, &cache->reqs, 0); + int ret; + + if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags)) + return 0; + +- if (test_bit(CACHEFILES_DEAD, &cache->flags)) +- return -EIO; ++ if (test_bit(CACHEFILES_DEAD, &cache->flags)) { ++ ret = -EIO; ++ goto out; ++ } + + req = kzalloc(sizeof(*req) + data_len, GFP_KERNEL); +- if (!req) +- return -ENOMEM; ++ if (!req) { ++ ret = -ENOMEM; ++ goto out; ++ } + + req->object = object; + init_completion(&req->done); +@@ -367,7 +408,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + /* coupled with the barrier in cachefiles_flush_reqs() */ + smp_mb(); + +- if (opcode != CACHEFILES_OP_OPEN && ++ if (opcode == CACHEFILES_OP_CLOSE && + !cachefiles_ondemand_object_is_open(object)) { + WARN_ON_ONCE(object->ondemand->ondemand_id == 0); + xas_unlock(&xas); +@@ -392,7 +433,15 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, + wake_up_all(&cache->daemon_pollwq); + wait_for_completion(&req->done); + ret = req->error; ++ kfree(req); ++ return ret; + out: ++ /* Reset the object to close state in error handling path. ++ * If error occurs after creating the anonymous fd, ++ * cachefiles_ondemand_fd_release() will set object to close. ++ */ ++ if (opcode == CACHEFILES_OP_OPEN) ++ cachefiles_ondemand_set_object_close(object); + kfree(req); + return ret; + } +@@ -439,7 +488,6 @@ static int cachefiles_ondemand_init_close_req(struct cachefiles_req *req, + if (!cachefiles_ondemand_object_is_open(object)) + return -ENOENT; + +- req->msg.object_id = object->ondemand->ondemand_id; + trace_cachefiles_ondemand_close(object, &req->msg); + return 0; + } +@@ -455,16 +503,7 @@ static int cachefiles_ondemand_init_read_req(struct cachefiles_req *req, + struct cachefiles_object *object = req->object; + struct cachefiles_read *load = (void *)req->msg.data; + struct cachefiles_read_ctx *read_ctx = private; +- int object_id = object->ondemand->ondemand_id; +- +- /* Stop enqueuing requests when daemon has closed anon_fd. */ +- if (!cachefiles_ondemand_object_is_open(object)) { +- WARN_ON_ONCE(object_id == 0); +- pr_info_once("READ: anonymous fd closed prematurely.\n"); +- return -EIO; +- } + +- req->msg.object_id = object_id; + load->off = read_ctx->off; + load->len = read_ctx->len; + trace_cachefiles_ondemand_read(object, &req->msg, load); +@@ -513,6 +552,7 @@ int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object, + return -ENOMEM; + + object->ondemand->object = object; ++ INIT_WORK(&object->ondemand->ondemand_work, ondemand_object_worker); + return 0; + } + +-- +2.43.0 + diff --git a/queue-6.1/clk-sifive-do-not-register-clkdevs-for-prci-clocks.patch b/queue-6.1/clk-sifive-do-not-register-clkdevs-for-prci-clocks.patch new file mode 100644 index 00000000000..8c702a57341 --- /dev/null +++ b/queue-6.1/clk-sifive-do-not-register-clkdevs-for-prci-clocks.patch @@ -0,0 +1,62 @@ +From 5677d68be93306c1f9e54c183e129cd1c05817e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 May 2024 17:14:12 -0700 +Subject: clk: sifive: Do not register clkdevs for PRCI clocks + +From: Samuel Holland + +[ Upstream commit 2607133196c35f31892ee199ce7ffa717bea4ad1 ] + +These clkdevs were unnecessary, because systems using this driver always +look up clocks using the devicetree. And as Russell King points out[1], +since the provided device name was truncated, lookups via clkdev would +never match. + +Recently, commit 8d532528ff6a ("clkdev: report over-sized strings when +creating clkdev entries") caused clkdev registration to fail due to the +truncation, and this now prevents the driver from probing. Fix the +driver by removing the clkdev registration. + +Link: https://lore.kernel.org/linux-clk/ZkfYqj+OcAxd9O2t@shell.armlinux.org.uk/ [1] +Fixes: 30b8e27e3b58 ("clk: sifive: add a driver for the SiFive FU540 PRCI IP block") +Fixes: 8d532528ff6a ("clkdev: report over-sized strings when creating clkdev entries") +Reported-by: Guenter Roeck +Closes: https://lore.kernel.org/linux-clk/7eda7621-0dde-4153-89e4-172e4c095d01@roeck-us.net/ +Suggested-by: Russell King +Signed-off-by: Samuel Holland +Link: https://lore.kernel.org/r/20240528001432.1200403-1-samuel.holland@sifive.com +Signed-off-by: Stephen Boyd +Signed-off-by: Sasha Levin +--- + drivers/clk/sifive/sifive-prci.c | 8 -------- + 1 file changed, 8 deletions(-) + +diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c +index 916d2fc28b9c1..39bfbd120e0bc 100644 +--- a/drivers/clk/sifive/sifive-prci.c ++++ b/drivers/clk/sifive/sifive-prci.c +@@ -4,7 +4,6 @@ + * Copyright (C) 2020 Zong Li + */ + +-#include + #include + #include + #include +@@ -536,13 +535,6 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd, + return r; + } + +- r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); +- if (r) { +- dev_warn(dev, "Failed to register clkdev for %s: %d\n", +- init.name, r); +- return r; +- } +- + pd->hw_clks.hws[i] = &pic->hw; + } + +-- +2.43.0 + diff --git a/queue-6.1/cxl-test-add-missing-vmalloc.h-for-tools-testing-cxl.patch b/queue-6.1/cxl-test-add-missing-vmalloc.h-for-tools-testing-cxl.patch new file mode 100644 index 00000000000..48738dada8c --- /dev/null +++ b/queue-6.1/cxl-test-add-missing-vmalloc.h-for-tools-testing-cxl.patch @@ -0,0 +1,55 @@ +From 4812a4a59816d53400938543e3767f4855d94d23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 May 2024 15:55:51 -0700 +Subject: cxl/test: Add missing vmalloc.h for tools/testing/cxl/test/mem.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Dave Jiang + +[ Upstream commit d55510527153d17a3af8cc2df69c04f95ae1350d ] + +tools/testing/cxl/test/mem.c uses vmalloc() and vfree() but does not +include linux/vmalloc.h. Kernel v6.10 made changes that causes the +currently included headers not depend on vmalloc.h and therefore +mem.c can no longer compile. Add linux/vmalloc.h to fix compile +issue. + + CC [M] tools/testing/cxl/test/mem.o +tools/testing/cxl/test/mem.c: In function ‘label_area_release’: +tools/testing/cxl/test/mem.c:1428:9: error: implicit declaration of function ‘vfree’; did you mean ‘kvfree’? [-Werror=implicit-function-declaration] + 1428 | vfree(lsa); + | ^~~~~ + | kvfree +tools/testing/cxl/test/mem.c: In function ‘cxl_mock_mem_probe’: +tools/testing/cxl/test/mem.c:1466:22: error: implicit declaration of function ‘vmalloc’; did you mean ‘kmalloc’? [-Werror=implicit-function-declaration] + 1466 | mdata->lsa = vmalloc(LSA_SIZE); + | ^~~~~~~ + | kmalloc + +Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") +Reviewed-by: Dan Williams +Reviewed-by: Alison Schofield +Link: https://lore.kernel.org/r/20240528225551.1025977-1-dave.jiang@intel.com +Signed-off-by: Dave Jiang +Signed-off-by: Sasha Levin +--- + tools/testing/cxl/test/mem.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c +index aa2df3a150518..6e9a89f54d94f 100644 +--- a/tools/testing/cxl/test/mem.c ++++ b/tools/testing/cxl/test/mem.c +@@ -3,6 +3,7 @@ + + #include + #include ++#include + #include + #include + #include +-- +2.43.0 + diff --git a/queue-6.1/drm-bridge-panel-fix-runtime-warning-on-panel-bridge.patch b/queue-6.1/drm-bridge-panel-fix-runtime-warning-on-panel-bridge.patch new file mode 100644 index 00000000000..550599e56b8 --- /dev/null +++ b/queue-6.1/drm-bridge-panel-fix-runtime-warning-on-panel-bridge.patch @@ -0,0 +1,51 @@ +From 51dc931b01bc7a35e66044e539b02fc16b578e44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 10 Jun 2024 11:27:39 +0100 +Subject: drm/bridge/panel: Fix runtime warning on panel bridge release + +From: Adam Miotk + +[ Upstream commit ce62600c4dbee8d43b02277669dd91785a9b81d9 ] + +Device managed panel bridge wrappers are created by calling to +drm_panel_bridge_add_typed() and registering a release handler for +clean-up when the device gets unbound. + +Since the memory for this bridge is also managed and linked to the panel +device, the release function should not try to free that memory. +Moreover, the call to devm_kfree() inside drm_panel_bridge_remove() will +fail in this case and emit a warning because the panel bridge resource +is no longer on the device resources list (it has been removed from +there before the call to release handlers). + +Fixes: 67022227ffb1 ("drm/bridge: Add a devm_ allocator for panel bridge.") +Signed-off-by: Adam Miotk +Signed-off-by: Maxime Ripard +Link: https://patchwork.freedesktop.org/patch/msgid/20240610102739.139852-1-adam.miotk@arm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/bridge/panel.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c +index 216af76d00427..cdfcdbecd4c80 100644 +--- a/drivers/gpu/drm/bridge/panel.c ++++ b/drivers/gpu/drm/bridge/panel.c +@@ -306,9 +306,12 @@ EXPORT_SYMBOL(drm_panel_bridge_set_orientation); + + static void devm_drm_panel_bridge_release(struct device *dev, void *res) + { +- struct drm_bridge **bridge = res; ++ struct drm_bridge *bridge = *(struct drm_bridge **)res; + +- drm_panel_bridge_remove(*bridge); ++ if (!bridge) ++ return; ++ ++ drm_bridge_remove(bridge); + } + + /** +-- +2.43.0 + diff --git a/queue-6.1/drm-komeda-check-for-error-valued-pointer.patch b/queue-6.1/drm-komeda-check-for-error-valued-pointer.patch new file mode 100644 index 00000000000..3790dde9d94 --- /dev/null +++ b/queue-6.1/drm-komeda-check-for-error-valued-pointer.patch @@ -0,0 +1,37 @@ +From f58642e0963ce332f8335e47f6133be6adb689c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 10 Jun 2024 11:20:56 +0100 +Subject: drm/komeda: check for error-valued pointer + +From: Amjad Ouled-Ameur + +[ Upstream commit b880018edd3a577e50366338194dee9b899947e0 ] + +komeda_pipeline_get_state() may return an error-valued pointer, thus +check the pointer for negative or null value before dereferencing. + +Fixes: 502932a03fce ("drm/komeda: Add the initial scaler support for CORE") +Signed-off-by: Amjad Ouled-Ameur +Signed-off-by: Maxime Ripard +Link: https://patchwork.freedesktop.org/patch/msgid/20240610102056.40406-1-amjad.ouled-ameur@arm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c +index 916f2c36bf2f7..e200decd00c6d 100644 +--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c ++++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c +@@ -259,7 +259,7 @@ komeda_component_get_avail_scaler(struct komeda_component *c, + u32 avail_scalers; + + pipe_st = komeda_pipeline_get_state(c->pipeline, state); +- if (!pipe_st) ++ if (IS_ERR_OR_NULL(pipe_st)) + return NULL; + + avail_scalers = (pipe_st->active_comps & KOMEDA_PIPELINE_SCALERS) ^ +-- +2.43.0 + diff --git a/queue-6.1/drm-vmwgfx-3d-disabled-should-not-effect-stdu-memory.patch b/queue-6.1/drm-vmwgfx-3d-disabled-should-not-effect-stdu-memory.patch new file mode 100644 index 00000000000..a29aad00178 --- /dev/null +++ b/queue-6.1/drm-vmwgfx-3d-disabled-should-not-effect-stdu-memory.patch @@ -0,0 +1,45 @@ +From 2aad74ed49bac4bc7c9d1385951ea099e8aad0d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 May 2024 13:47:18 -0500 +Subject: drm/vmwgfx: 3D disabled should not effect STDU memory limits + +From: Ian Forbes + +[ Upstream commit fb5e19d2dd03eb995ccd468d599b2337f7f66555 ] + +This limit became a hard cap starting with the change referenced below. +Surface creation on the device will fail if the requested size is larger +than this limit so altering the value arbitrarily will expose modes that +are too large for the device's hard limits. + +Fixes: 7ebb47c9f9ab ("drm/vmwgfx: Read new register for GB memory when available") + +Signed-off-by: Ian Forbes +Signed-off-by: Zack Rusin +Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-3-ian.forbes@broadcom.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 0a75084cd32a2..be27f9a3bf67b 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -938,13 +938,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) + vmw_read(dev_priv, + SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); + +- /* +- * Workaround for low memory 2D VMs to compensate for the +- * allocation taken by fbdev +- */ +- if (!(dev_priv->capabilities & SVGA_CAP_3D)) +- mem_size *= 3; +- + dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; + dev_priv->max_primary_mem = + vmw_read(dev_priv, SVGA_REG_MAX_PRIMARY_MEM); +-- +2.43.0 + diff --git a/queue-6.1/drm-vmwgfx-filter-modes-which-exceed-graphics-memory.patch b/queue-6.1/drm-vmwgfx-filter-modes-which-exceed-graphics-memory.patch new file mode 100644 index 00000000000..7cbe0ec2441 --- /dev/null +++ b/queue-6.1/drm-vmwgfx-filter-modes-which-exceed-graphics-memory.patch @@ -0,0 +1,106 @@ +From 4aa6e2c94091f9159f9829e16a3d020dce2cd325 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 May 2024 13:47:17 -0500 +Subject: drm/vmwgfx: Filter modes which exceed graphics memory + +From: Ian Forbes + +[ Upstream commit 426826933109093503e7ef15d49348fc5ab505fe ] + +SVGA requires individual surfaces to fit within graphics memory +(max_mob_pages) which means that modes with a final buffer size that would +exceed graphics memory must be pruned otherwise creation will fail. + +Additionally llvmpipe requires its buffer height and width to be a multiple +of its tile size which is 64. As a result we have to anticipate that +llvmpipe will round up the mode size passed to it by the compositor when +it creates buffers and filter modes where this rounding exceeds graphics +memory. + +This fixes an issue where VMs with low graphics memory (< 64MiB) configured +with high resolution mode boot to a black screen because surface creation +fails. + +Fixes: d947d1b71deb ("drm/vmwgfx: Add and connect connector helper function") +Signed-off-by: Ian Forbes +Signed-off-by: Zack Rusin +Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-2-ian.forbes@broadcom.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 45 ++++++++++++++++++++++++++-- + 1 file changed, 43 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +index b3e70ace6d9b0..6dd33d1258d11 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +@@ -40,7 +40,14 @@ + #define vmw_connector_to_stdu(x) \ + container_of(x, struct vmw_screen_target_display_unit, base.connector) + +- ++/* ++ * Some renderers such as llvmpipe will align the width and height of their ++ * buffers to match their tile size. We need to keep this in mind when exposing ++ * modes to userspace so that this possible over-allocation will not exceed ++ * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the ++ * tile size of current renderers. ++ */ ++#define GPU_TILE_SIZE 64 + + enum stdu_content_type { + SAME_AS_DISPLAY = 0, +@@ -972,7 +979,41 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector) + vmw_stdu_destroy(vmw_connector_to_stdu(connector)); + } + ++static enum drm_mode_status ++vmw_stdu_connector_mode_valid(struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ enum drm_mode_status ret; ++ struct drm_device *dev = connector->dev; ++ struct vmw_private *dev_priv = vmw_priv(dev); ++ u64 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4; ++ /* Align width and height to account for GPU tile over-alignment */ ++ u64 required_mem = ALIGN(mode->hdisplay, GPU_TILE_SIZE) * ++ ALIGN(mode->vdisplay, GPU_TILE_SIZE) * ++ assumed_cpp; ++ required_mem = ALIGN(required_mem, PAGE_SIZE); ++ ++ ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width, ++ dev_priv->stdu_max_height); ++ if (ret != MODE_OK) ++ return ret; + ++ ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, ++ dev_priv->texture_max_height); ++ if (ret != MODE_OK) ++ return ret; ++ ++ if (required_mem > dev_priv->max_primary_mem) ++ return MODE_MEM; ++ ++ if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE) ++ return MODE_MEM; ++ ++ if (required_mem > dev_priv->max_mob_size) ++ return MODE_MEM; ++ ++ return MODE_OK; ++} + + static const struct drm_connector_funcs vmw_stdu_connector_funcs = { + .dpms = vmw_du_connector_dpms, +@@ -988,7 +1029,7 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { + static const struct + drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { + .get_modes = vmw_connector_get_modes, +- .mode_valid = vmw_connector_mode_valid ++ .mode_valid = vmw_stdu_connector_mode_valid + }; + + +-- +2.43.0 + diff --git a/queue-6.1/drm-vmwgfx-port-the-framebuffer-code-to-drm-fb-helpe.patch b/queue-6.1/drm-vmwgfx-port-the-framebuffer-code-to-drm-fb-helpe.patch new file mode 100644 index 00000000000..86e814e0b2e --- /dev/null +++ b/queue-6.1/drm-vmwgfx-port-the-framebuffer-code-to-drm-fb-helpe.patch @@ -0,0 +1,1294 @@ +From 4ff94900f302b23052678fa4e2437c688ae4eafd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 22 Oct 2022 00:02:32 -0400 +Subject: drm/vmwgfx: Port the framebuffer code to drm fb helpers + +From: Zack Rusin + +[ Upstream commit df42523c12f8d58a41f547f471b46deffd18c203 ] + +Instead of using vmwgfx specific framebuffer implementation use the drm +fb helpers. There's no change in functionality, the only difference +is a reduction in the amount of code inside the vmwgfx module. + +drm fb helpers do not deal correctly with changes in crtc preferred mode +at runtime, but the old fb code wasn't dealing with it either. +Same situation applies to high-res fb consoles - the old code was +limited to 1176x885 because it was checking for legacy/deprecated +memory limites, the drm fb helpers are limited to the initial resolution +set on fb due to first problem (drm fb helpers being unable to handle +hotplug crtc preferred mode changes). + +This also removes the kernel config for disabling fb support which hasn't +been used or supported in a very long time. + +Signed-off-by: Zack Rusin +Reviewed-by: Maaz Mombasawala +Reviewed-by: Martin Krastev +Reviewed-by: Thomas Zimmermann +Link: https://patchwork.freedesktop.org/patch/msgid/20221022040236.616490-14-zack@kde.org +Stable-dep-of: 426826933109 ("drm/vmwgfx: Filter modes which exceed graphics memory") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/vmwgfx/Kconfig | 7 - + drivers/gpu/drm/vmwgfx/Makefile | 2 - + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 58 +- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 35 +- + drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 831 ---------------------------- + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 79 +-- + drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 7 - + 7 files changed, 28 insertions(+), 991 deletions(-) + delete mode 100644 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c + +diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig +index a4fabe208d9f0..faddae3d6ac2e 100644 +--- a/drivers/gpu/drm/vmwgfx/Kconfig ++++ b/drivers/gpu/drm/vmwgfx/Kconfig +@@ -16,13 +16,6 @@ config DRM_VMWGFX + virtual hardware. + The compiled module will be called "vmwgfx.ko". + +-config DRM_VMWGFX_FBCON +- depends on DRM_VMWGFX && DRM_FBDEV_EMULATION +- bool "Enable framebuffer console under vmwgfx by default" +- help +- Choose this option if you are shipping a new vmwgfx +- userspace driver that supports using the kernel driver. +- + config DRM_VMWGFX_MKSSTATS + bool "Enable mksGuestStats instrumentation of vmwgfx by default" + depends on DRM_VMWGFX +diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile +index 68e350f410ad3..2a644f035597f 100644 +--- a/drivers/gpu/drm/vmwgfx/Makefile ++++ b/drivers/gpu/drm/vmwgfx/Makefile +@@ -12,6 +12,4 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ + vmwgfx_devcaps.o ttm_object.o vmwgfx_system_manager.o \ + vmwgfx_gem.o + +-vmwgfx-$(CONFIG_DRM_FBDEV_EMULATION) += vmwgfx_fb.o +- + obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 53f63ad656a41..0a75084cd32a2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -35,6 +35,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -52,9 +53,6 @@ + + #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" + +-#define VMW_MIN_INITIAL_WIDTH 800 +-#define VMW_MIN_INITIAL_HEIGHT 600 +- + /* + * Fully encoded drm commands. Might move to vmw_drm.h + */ +@@ -265,7 +263,6 @@ static const struct pci_device_id vmw_pci_id_list[] = { + }; + MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); + +-static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); + static int vmw_restrict_iommu; + static int vmw_force_coherent; + static int vmw_restrict_dma_mask; +@@ -275,8 +272,6 @@ static int vmw_probe(struct pci_dev *, const struct pci_device_id *); + static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + void *ptr); + +-MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); +-module_param_named(enable_fbdev, enable_fbdev, int, 0600); + MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); + module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); + MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); +@@ -626,8 +621,8 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) + width = vmw_read(dev_priv, SVGA_REG_WIDTH); + height = vmw_read(dev_priv, SVGA_REG_HEIGHT); + +- width = max_t(uint32_t, width, VMW_MIN_INITIAL_WIDTH); +- height = max_t(uint32_t, height, VMW_MIN_INITIAL_HEIGHT); ++ width = max_t(uint32_t, width, VMWGFX_MIN_INITIAL_WIDTH); ++ height = max_t(uint32_t, height, VMWGFX_MIN_INITIAL_HEIGHT); + + if (width > dev_priv->fb_max_width || + height > dev_priv->fb_max_height) { +@@ -636,8 +631,8 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) + * This is a host error and shouldn't occur. + */ + +- width = VMW_MIN_INITIAL_WIDTH; +- height = VMW_MIN_INITIAL_HEIGHT; ++ width = VMWGFX_MIN_INITIAL_WIDTH; ++ height = VMWGFX_MIN_INITIAL_HEIGHT; + } + + dev_priv->initial_width = width; +@@ -887,9 +882,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) + + dev_priv->assume_16bpp = !!vmw_assume_16bpp; + +- dev_priv->enable_fb = enable_fbdev; +- +- + dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); + vmw_print_bitmap(&dev_priv->drm, "Capabilities", + dev_priv->capabilities, +@@ -1136,12 +1128,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) + VMWGFX_DRIVER_PATCHLEVEL, UTS_RELEASE); + vmw_write_driver_id(dev_priv); + +- if (dev_priv->enable_fb) { +- vmw_fifo_resource_inc(dev_priv); +- vmw_svga_enable(dev_priv); +- vmw_fb_init(dev_priv); +- } +- + dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier; + register_pm_notifier(&dev_priv->pm_nb); + +@@ -1188,12 +1174,9 @@ static void vmw_driver_unload(struct drm_device *dev) + unregister_pm_notifier(&dev_priv->pm_nb); + + vmw_sw_context_fini(dev_priv); +- if (dev_priv->enable_fb) { +- vmw_fb_off(dev_priv); +- vmw_fb_close(dev_priv); +- vmw_fifo_resource_dec(dev_priv); +- vmw_svga_disable(dev_priv); +- } ++ vmw_fifo_resource_dec(dev_priv); ++ ++ vmw_svga_disable(dev_priv); + + vmw_kms_close(dev_priv); + vmw_overlay_close(dev_priv); +@@ -1331,8 +1314,6 @@ static void vmw_master_drop(struct drm_device *dev, + struct vmw_private *dev_priv = vmw_priv(dev); + + vmw_kms_legacy_hotspot_clear(dev_priv); +- if (!dev_priv->enable_fb) +- vmw_svga_disable(dev_priv); + } + + /** +@@ -1528,25 +1509,19 @@ static int vmw_pm_freeze(struct device *kdev) + DRM_ERROR("Failed to freeze modesetting.\n"); + return ret; + } +- if (dev_priv->enable_fb) +- vmw_fb_off(dev_priv); + + vmw_execbuf_release_pinned_bo(dev_priv); + vmw_resource_evict_all(dev_priv); + vmw_release_device_early(dev_priv); + while (ttm_device_swapout(&dev_priv->bdev, &ctx, GFP_KERNEL) > 0); +- if (dev_priv->enable_fb) +- vmw_fifo_resource_dec(dev_priv); ++ vmw_fifo_resource_dec(dev_priv); + if (atomic_read(&dev_priv->num_fifo_resources) != 0) { + DRM_ERROR("Can't hibernate while 3D resources are active.\n"); +- if (dev_priv->enable_fb) +- vmw_fifo_resource_inc(dev_priv); ++ vmw_fifo_resource_inc(dev_priv); + WARN_ON(vmw_request_device_late(dev_priv)); + dev_priv->suspend_locked = false; + if (dev_priv->suspend_state) + vmw_kms_resume(dev); +- if (dev_priv->enable_fb) +- vmw_fb_on(dev_priv); + return -EBUSY; + } + +@@ -1566,24 +1541,19 @@ static int vmw_pm_restore(struct device *kdev) + + vmw_detect_version(dev_priv); + +- if (dev_priv->enable_fb) +- vmw_fifo_resource_inc(dev_priv); ++ vmw_fifo_resource_inc(dev_priv); + + ret = vmw_request_device(dev_priv); + if (ret) + return ret; + +- if (dev_priv->enable_fb) +- __vmw_svga_enable(dev_priv); ++ __vmw_svga_enable(dev_priv); + + vmw_fence_fifo_up(dev_priv->fman); + dev_priv->suspend_locked = false; + if (dev_priv->suspend_state) + vmw_kms_resume(&dev_priv->drm); + +- if (dev_priv->enable_fb) +- vmw_fb_on(dev_priv); +- + return 0; + } + +@@ -1674,6 +1644,10 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (ret) + goto out_unload; + ++ vmw_fifo_resource_inc(vmw); ++ vmw_svga_enable(vmw); ++ drm_fbdev_generic_setup(&vmw->drm, 0); ++ + vmw_debugfs_gem_init(vmw); + vmw_debugfs_resource_managers_init(vmw); + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index 136f1cdcf8cdf..00d9e58b7e149 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -62,6 +62,9 @@ + #define VMWGFX_MAX_DISPLAYS 16 + #define VMWGFX_CMD_BOUNCE_INIT_SIZE 32768 + ++#define VMWGFX_MIN_INITIAL_WIDTH 1280 ++#define VMWGFX_MIN_INITIAL_HEIGHT 800 ++ + #define VMWGFX_PCI_ID_SVGA2 0x0405 + #define VMWGFX_PCI_ID_SVGA3 0x0406 + +@@ -551,7 +554,6 @@ struct vmw_private { + * Framebuffer info. + */ + +- void *fb_info; + enum vmw_display_unit_type active_display_unit; + struct vmw_legacy_display *ldu_priv; + struct vmw_overlay *overlay_priv; +@@ -610,8 +612,6 @@ struct vmw_private { + struct mutex cmdbuf_mutex; + struct mutex binding_mutex; + +- bool enable_fb; +- + /** + * PM management. + */ +@@ -1178,35 +1178,6 @@ extern void vmw_generic_waiter_add(struct vmw_private *dev_priv, u32 flag, + extern void vmw_generic_waiter_remove(struct vmw_private *dev_priv, + u32 flag, int *waiter_count); + +- +-/** +- * Kernel framebuffer - vmwgfx_fb.c +- */ +- +-#ifdef CONFIG_DRM_FBDEV_EMULATION +-int vmw_fb_init(struct vmw_private *vmw_priv); +-int vmw_fb_close(struct vmw_private *dev_priv); +-int vmw_fb_off(struct vmw_private *vmw_priv); +-int vmw_fb_on(struct vmw_private *vmw_priv); +-#else +-static inline int vmw_fb_init(struct vmw_private *vmw_priv) +-{ +- return 0; +-} +-static inline int vmw_fb_close(struct vmw_private *dev_priv) +-{ +- return 0; +-} +-static inline int vmw_fb_off(struct vmw_private *vmw_priv) +-{ +- return 0; +-} +-static inline int vmw_fb_on(struct vmw_private *vmw_priv) +-{ +- return 0; +-} +-#endif +- + /** + * Kernel modesetting - vmwgfx_kms.c + */ +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +deleted file mode 100644 +index 5b85b477e4c69..0000000000000 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c ++++ /dev/null +@@ -1,831 +0,0 @@ +-/************************************************************************** +- * +- * Copyright © 2007 David Airlie +- * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA +- * All Rights Reserved. +- * +- * Permission is hereby granted, free of charge, to any person obtaining a +- * copy of this software and associated documentation files (the +- * "Software"), to deal in the Software without restriction, including +- * without limitation the rights to use, copy, modify, merge, publish, +- * distribute, sub license, and/or sell copies of the Software, and to +- * permit persons to whom the Software is furnished to do so, subject to +- * the following conditions: +- * +- * The above copyright notice and this permission notice (including the +- * next paragraph) shall be included in all copies or substantial portions +- * of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +- * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, +- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +- * USE OR OTHER DEALINGS IN THE SOFTWARE. +- * +- **************************************************************************/ +- +-#include +-#include +- +-#include +-#include +- +-#include "vmwgfx_drv.h" +-#include "vmwgfx_kms.h" +- +-#define VMW_DIRTY_DELAY (HZ / 30) +- +-struct vmw_fb_par { +- struct vmw_private *vmw_priv; +- +- void *vmalloc; +- +- struct mutex bo_mutex; +- struct vmw_buffer_object *vmw_bo; +- unsigned bo_size; +- struct drm_framebuffer *set_fb; +- struct drm_display_mode *set_mode; +- u32 fb_x; +- u32 fb_y; +- bool bo_iowrite; +- +- u32 pseudo_palette[17]; +- +- unsigned max_width; +- unsigned max_height; +- +- struct { +- spinlock_t lock; +- bool active; +- unsigned x1; +- unsigned y1; +- unsigned x2; +- unsigned y2; +- } dirty; +- +- struct drm_crtc *crtc; +- struct drm_connector *con; +- struct delayed_work local_work; +-}; +- +-static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green, +- unsigned blue, unsigned transp, +- struct fb_info *info) +-{ +- struct vmw_fb_par *par = info->par; +- u32 *pal = par->pseudo_palette; +- +- if (regno > 15) { +- DRM_ERROR("Bad regno %u.\n", regno); +- return 1; +- } +- +- switch (par->set_fb->format->depth) { +- case 24: +- case 32: +- pal[regno] = ((red & 0xff00) << 8) | +- (green & 0xff00) | +- ((blue & 0xff00) >> 8); +- break; +- default: +- DRM_ERROR("Bad depth %u, bpp %u.\n", +- par->set_fb->format->depth, +- par->set_fb->format->cpp[0] * 8); +- return 1; +- } +- +- return 0; +-} +- +-static int vmw_fb_check_var(struct fb_var_screeninfo *var, +- struct fb_info *info) +-{ +- int depth = var->bits_per_pixel; +- struct vmw_fb_par *par = info->par; +- struct vmw_private *vmw_priv = par->vmw_priv; +- +- switch (var->bits_per_pixel) { +- case 32: +- depth = (var->transp.length > 0) ? 32 : 24; +- break; +- default: +- DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel); +- return -EINVAL; +- } +- +- switch (depth) { +- case 24: +- var->red.offset = 16; +- var->green.offset = 8; +- var->blue.offset = 0; +- var->red.length = 8; +- var->green.length = 8; +- var->blue.length = 8; +- var->transp.length = 0; +- var->transp.offset = 0; +- break; +- case 32: +- var->red.offset = 16; +- var->green.offset = 8; +- var->blue.offset = 0; +- var->red.length = 8; +- var->green.length = 8; +- var->blue.length = 8; +- var->transp.length = 8; +- var->transp.offset = 24; +- break; +- default: +- DRM_ERROR("Bad depth %u.\n", depth); +- return -EINVAL; +- } +- +- if ((var->xoffset + var->xres) > par->max_width || +- (var->yoffset + var->yres) > par->max_height) { +- DRM_ERROR("Requested geom can not fit in framebuffer\n"); +- return -EINVAL; +- } +- +- if (!vmw_kms_validate_mode_vram(vmw_priv, +- var->xres * var->bits_per_pixel/8, +- var->yoffset + var->yres)) { +- DRM_ERROR("Requested geom can not fit in framebuffer\n"); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int vmw_fb_blank(int blank, struct fb_info *info) +-{ +- return 0; +-} +- +-/** +- * vmw_fb_dirty_flush - flush dirty regions to the kms framebuffer +- * +- * @work: The struct work_struct associated with this task. +- * +- * This function flushes the dirty regions of the vmalloc framebuffer to the +- * kms framebuffer, and if the kms framebuffer is visible, also updated the +- * corresponding displays. Note that this function runs even if the kms +- * framebuffer is not bound to a crtc and thus not visible, but it's turned +- * off during hibernation using the par->dirty.active bool. +- */ +-static void vmw_fb_dirty_flush(struct work_struct *work) +-{ +- struct vmw_fb_par *par = container_of(work, struct vmw_fb_par, +- local_work.work); +- struct vmw_private *vmw_priv = par->vmw_priv; +- struct fb_info *info = vmw_priv->fb_info; +- unsigned long irq_flags; +- s32 dst_x1, dst_x2, dst_y1, dst_y2, w = 0, h = 0; +- u32 cpp, max_x, max_y; +- struct drm_clip_rect clip; +- struct drm_framebuffer *cur_fb; +- u8 *src_ptr, *dst_ptr; +- struct vmw_buffer_object *vbo = par->vmw_bo; +- void *virtual; +- +- if (!READ_ONCE(par->dirty.active)) +- return; +- +- mutex_lock(&par->bo_mutex); +- cur_fb = par->set_fb; +- if (!cur_fb) +- goto out_unlock; +- +- (void) ttm_bo_reserve(&vbo->base, false, false, NULL); +- virtual = vmw_bo_map_and_cache(vbo); +- if (!virtual) +- goto out_unreserve; +- +- spin_lock_irqsave(&par->dirty.lock, irq_flags); +- if (!par->dirty.active) { +- spin_unlock_irqrestore(&par->dirty.lock, irq_flags); +- goto out_unreserve; +- } +- +- /* +- * Handle panning when copying from vmalloc to framebuffer. +- * Clip dirty area to framebuffer. +- */ +- cpp = cur_fb->format->cpp[0]; +- max_x = par->fb_x + cur_fb->width; +- max_y = par->fb_y + cur_fb->height; +- +- dst_x1 = par->dirty.x1 - par->fb_x; +- dst_y1 = par->dirty.y1 - par->fb_y; +- dst_x1 = max_t(s32, dst_x1, 0); +- dst_y1 = max_t(s32, dst_y1, 0); +- +- dst_x2 = par->dirty.x2 - par->fb_x; +- dst_y2 = par->dirty.y2 - par->fb_y; +- dst_x2 = min_t(s32, dst_x2, max_x); +- dst_y2 = min_t(s32, dst_y2, max_y); +- w = dst_x2 - dst_x1; +- h = dst_y2 - dst_y1; +- w = max_t(s32, 0, w); +- h = max_t(s32, 0, h); +- +- par->dirty.x1 = par->dirty.x2 = 0; +- par->dirty.y1 = par->dirty.y2 = 0; +- spin_unlock_irqrestore(&par->dirty.lock, irq_flags); +- +- if (w && h) { +- dst_ptr = (u8 *)virtual + +- (dst_y1 * par->set_fb->pitches[0] + dst_x1 * cpp); +- src_ptr = (u8 *)par->vmalloc + +- ((dst_y1 + par->fb_y) * info->fix.line_length + +- (dst_x1 + par->fb_x) * cpp); +- +- while (h-- > 0) { +- memcpy(dst_ptr, src_ptr, w*cpp); +- dst_ptr += par->set_fb->pitches[0]; +- src_ptr += info->fix.line_length; +- } +- +- clip.x1 = dst_x1; +- clip.x2 = dst_x2; +- clip.y1 = dst_y1; +- clip.y2 = dst_y2; +- } +- +-out_unreserve: +- ttm_bo_unreserve(&vbo->base); +- if (w && h) { +- WARN_ON_ONCE(par->set_fb->funcs->dirty(cur_fb, NULL, 0, 0, +- &clip, 1)); +- vmw_cmd_flush(vmw_priv, false); +- } +-out_unlock: +- mutex_unlock(&par->bo_mutex); +-} +- +-static void vmw_fb_dirty_mark(struct vmw_fb_par *par, +- unsigned x1, unsigned y1, +- unsigned width, unsigned height) +-{ +- unsigned long flags; +- unsigned x2 = x1 + width; +- unsigned y2 = y1 + height; +- +- spin_lock_irqsave(&par->dirty.lock, flags); +- if (par->dirty.x1 == par->dirty.x2) { +- par->dirty.x1 = x1; +- par->dirty.y1 = y1; +- par->dirty.x2 = x2; +- par->dirty.y2 = y2; +- /* if we are active start the dirty work +- * we share the work with the defio system */ +- if (par->dirty.active) +- schedule_delayed_work(&par->local_work, +- VMW_DIRTY_DELAY); +- } else { +- if (x1 < par->dirty.x1) +- par->dirty.x1 = x1; +- if (y1 < par->dirty.y1) +- par->dirty.y1 = y1; +- if (x2 > par->dirty.x2) +- par->dirty.x2 = x2; +- if (y2 > par->dirty.y2) +- par->dirty.y2 = y2; +- } +- spin_unlock_irqrestore(&par->dirty.lock, flags); +-} +- +-static int vmw_fb_pan_display(struct fb_var_screeninfo *var, +- struct fb_info *info) +-{ +- struct vmw_fb_par *par = info->par; +- +- if ((var->xoffset + var->xres) > var->xres_virtual || +- (var->yoffset + var->yres) > var->yres_virtual) { +- DRM_ERROR("Requested panning can not fit in framebuffer\n"); +- return -EINVAL; +- } +- +- mutex_lock(&par->bo_mutex); +- par->fb_x = var->xoffset; +- par->fb_y = var->yoffset; +- if (par->set_fb) +- vmw_fb_dirty_mark(par, par->fb_x, par->fb_y, par->set_fb->width, +- par->set_fb->height); +- mutex_unlock(&par->bo_mutex); +- +- return 0; +-} +- +-static void vmw_deferred_io(struct fb_info *info, struct list_head *pagereflist) +-{ +- struct vmw_fb_par *par = info->par; +- unsigned long start, end, min, max; +- unsigned long flags; +- struct fb_deferred_io_pageref *pageref; +- int y1, y2; +- +- min = ULONG_MAX; +- max = 0; +- list_for_each_entry(pageref, pagereflist, list) { +- start = pageref->offset; +- end = start + PAGE_SIZE - 1; +- min = min(min, start); +- max = max(max, end); +- } +- +- if (min < max) { +- y1 = min / info->fix.line_length; +- y2 = (max / info->fix.line_length) + 1; +- +- spin_lock_irqsave(&par->dirty.lock, flags); +- par->dirty.x1 = 0; +- par->dirty.y1 = y1; +- par->dirty.x2 = info->var.xres; +- par->dirty.y2 = y2; +- spin_unlock_irqrestore(&par->dirty.lock, flags); +- +- /* +- * Since we've already waited on this work once, try to +- * execute asap. +- */ +- cancel_delayed_work(&par->local_work); +- schedule_delayed_work(&par->local_work, 0); +- } +-}; +- +-static struct fb_deferred_io vmw_defio = { +- .delay = VMW_DIRTY_DELAY, +- .deferred_io = vmw_deferred_io, +-}; +- +-/* +- * Draw code +- */ +- +-static void vmw_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +-{ +- cfb_fillrect(info, rect); +- vmw_fb_dirty_mark(info->par, rect->dx, rect->dy, +- rect->width, rect->height); +-} +- +-static void vmw_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) +-{ +- cfb_copyarea(info, region); +- vmw_fb_dirty_mark(info->par, region->dx, region->dy, +- region->width, region->height); +-} +- +-static void vmw_fb_imageblit(struct fb_info *info, const struct fb_image *image) +-{ +- cfb_imageblit(info, image); +- vmw_fb_dirty_mark(info->par, image->dx, image->dy, +- image->width, image->height); +-} +- +-/* +- * Bring up code +- */ +- +-static int vmw_fb_create_bo(struct vmw_private *vmw_priv, +- size_t size, struct vmw_buffer_object **out) +-{ +- struct vmw_buffer_object *vmw_bo; +- int ret; +- +- ret = vmw_bo_create(vmw_priv, size, +- &vmw_sys_placement, +- false, false, +- &vmw_bo_bo_free, &vmw_bo); +- if (unlikely(ret != 0)) +- return ret; +- +- *out = vmw_bo; +- +- return ret; +-} +- +-static int vmw_fb_compute_depth(struct fb_var_screeninfo *var, +- int *depth) +-{ +- switch (var->bits_per_pixel) { +- case 32: +- *depth = (var->transp.length > 0) ? 32 : 24; +- break; +- default: +- DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int vmwgfx_set_config_internal(struct drm_mode_set *set) +-{ +- struct drm_crtc *crtc = set->crtc; +- struct drm_modeset_acquire_ctx ctx; +- int ret; +- +- drm_modeset_acquire_init(&ctx, 0); +- +-restart: +- ret = crtc->funcs->set_config(set, &ctx); +- +- if (ret == -EDEADLK) { +- drm_modeset_backoff(&ctx); +- goto restart; +- } +- +- drm_modeset_drop_locks(&ctx); +- drm_modeset_acquire_fini(&ctx); +- +- return ret; +-} +- +-static int vmw_fb_kms_detach(struct vmw_fb_par *par, +- bool detach_bo, +- bool unref_bo) +-{ +- struct drm_framebuffer *cur_fb = par->set_fb; +- int ret; +- +- /* Detach the KMS framebuffer from crtcs */ +- if (par->set_mode) { +- struct drm_mode_set set; +- +- set.crtc = par->crtc; +- set.x = 0; +- set.y = 0; +- set.mode = NULL; +- set.fb = NULL; +- set.num_connectors = 0; +- set.connectors = &par->con; +- ret = vmwgfx_set_config_internal(&set); +- if (ret) { +- DRM_ERROR("Could not unset a mode.\n"); +- return ret; +- } +- drm_mode_destroy(&par->vmw_priv->drm, par->set_mode); +- par->set_mode = NULL; +- } +- +- if (cur_fb) { +- drm_framebuffer_put(cur_fb); +- par->set_fb = NULL; +- } +- +- if (par->vmw_bo && detach_bo && unref_bo) +- vmw_bo_unreference(&par->vmw_bo); +- +- return 0; +-} +- +-static int vmw_fb_kms_framebuffer(struct fb_info *info) +-{ +- struct drm_mode_fb_cmd2 mode_cmd = {0}; +- struct vmw_fb_par *par = info->par; +- struct fb_var_screeninfo *var = &info->var; +- struct drm_framebuffer *cur_fb; +- struct vmw_framebuffer *vfb; +- int ret = 0, depth; +- size_t new_bo_size; +- +- ret = vmw_fb_compute_depth(var, &depth); +- if (ret) +- return ret; +- +- mode_cmd.width = var->xres; +- mode_cmd.height = var->yres; +- mode_cmd.pitches[0] = ((var->bits_per_pixel + 7) / 8) * mode_cmd.width; +- mode_cmd.pixel_format = +- drm_mode_legacy_fb_format(var->bits_per_pixel, depth); +- +- cur_fb = par->set_fb; +- if (cur_fb && cur_fb->width == mode_cmd.width && +- cur_fb->height == mode_cmd.height && +- cur_fb->format->format == mode_cmd.pixel_format && +- cur_fb->pitches[0] == mode_cmd.pitches[0]) +- return 0; +- +- /* Need new buffer object ? */ +- new_bo_size = (size_t) mode_cmd.pitches[0] * (size_t) mode_cmd.height; +- ret = vmw_fb_kms_detach(par, +- par->bo_size < new_bo_size || +- par->bo_size > 2*new_bo_size, +- true); +- if (ret) +- return ret; +- +- if (!par->vmw_bo) { +- ret = vmw_fb_create_bo(par->vmw_priv, new_bo_size, +- &par->vmw_bo); +- if (ret) { +- DRM_ERROR("Failed creating a buffer object for " +- "fbdev.\n"); +- return ret; +- } +- par->bo_size = new_bo_size; +- } +- +- vfb = vmw_kms_new_framebuffer(par->vmw_priv, par->vmw_bo, NULL, +- true, &mode_cmd); +- if (IS_ERR(vfb)) +- return PTR_ERR(vfb); +- +- par->set_fb = &vfb->base; +- +- return 0; +-} +- +-static int vmw_fb_set_par(struct fb_info *info) +-{ +- struct vmw_fb_par *par = info->par; +- struct vmw_private *vmw_priv = par->vmw_priv; +- struct drm_mode_set set; +- struct fb_var_screeninfo *var = &info->var; +- struct drm_display_mode new_mode = { DRM_MODE("fb_mode", +- DRM_MODE_TYPE_DRIVER, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) +- }; +- struct drm_display_mode *mode; +- int ret; +- +- mode = drm_mode_duplicate(&vmw_priv->drm, &new_mode); +- if (!mode) { +- DRM_ERROR("Could not create new fb mode.\n"); +- return -ENOMEM; +- } +- +- mode->hdisplay = var->xres; +- mode->vdisplay = var->yres; +- vmw_guess_mode_timing(mode); +- +- if (!vmw_kms_validate_mode_vram(vmw_priv, +- mode->hdisplay * +- DIV_ROUND_UP(var->bits_per_pixel, 8), +- mode->vdisplay)) { +- drm_mode_destroy(&vmw_priv->drm, mode); +- return -EINVAL; +- } +- +- mutex_lock(&par->bo_mutex); +- ret = vmw_fb_kms_framebuffer(info); +- if (ret) +- goto out_unlock; +- +- par->fb_x = var->xoffset; +- par->fb_y = var->yoffset; +- +- set.crtc = par->crtc; +- set.x = 0; +- set.y = 0; +- set.mode = mode; +- set.fb = par->set_fb; +- set.num_connectors = 1; +- set.connectors = &par->con; +- +- ret = vmwgfx_set_config_internal(&set); +- if (ret) +- goto out_unlock; +- +- vmw_fb_dirty_mark(par, par->fb_x, par->fb_y, +- par->set_fb->width, par->set_fb->height); +- +- /* If there already was stuff dirty we wont +- * schedule a new work, so lets do it now */ +- +- schedule_delayed_work(&par->local_work, 0); +- +-out_unlock: +- if (par->set_mode) +- drm_mode_destroy(&vmw_priv->drm, par->set_mode); +- par->set_mode = mode; +- +- mutex_unlock(&par->bo_mutex); +- +- return ret; +-} +- +- +-static const struct fb_ops vmw_fb_ops = { +- .owner = THIS_MODULE, +- .fb_check_var = vmw_fb_check_var, +- .fb_set_par = vmw_fb_set_par, +- .fb_setcolreg = vmw_fb_setcolreg, +- .fb_fillrect = vmw_fb_fillrect, +- .fb_copyarea = vmw_fb_copyarea, +- .fb_imageblit = vmw_fb_imageblit, +- .fb_pan_display = vmw_fb_pan_display, +- .fb_blank = vmw_fb_blank, +- .fb_mmap = fb_deferred_io_mmap, +-}; +- +-int vmw_fb_init(struct vmw_private *vmw_priv) +-{ +- struct device *device = vmw_priv->drm.dev; +- struct vmw_fb_par *par; +- struct fb_info *info; +- unsigned fb_width, fb_height; +- unsigned int fb_bpp, fb_pitch, fb_size; +- struct drm_display_mode *init_mode; +- int ret; +- +- fb_bpp = 32; +- +- /* XXX As shouldn't these be as well. */ +- fb_width = min(vmw_priv->fb_max_width, (unsigned)2048); +- fb_height = min(vmw_priv->fb_max_height, (unsigned)2048); +- +- fb_pitch = fb_width * fb_bpp / 8; +- fb_size = fb_pitch * fb_height; +- +- info = framebuffer_alloc(sizeof(*par), device); +- if (!info) +- return -ENOMEM; +- +- /* +- * Par +- */ +- vmw_priv->fb_info = info; +- par = info->par; +- memset(par, 0, sizeof(*par)); +- INIT_DELAYED_WORK(&par->local_work, &vmw_fb_dirty_flush); +- par->vmw_priv = vmw_priv; +- par->vmalloc = NULL; +- par->max_width = fb_width; +- par->max_height = fb_height; +- +- ret = vmw_kms_fbdev_init_data(vmw_priv, 0, par->max_width, +- par->max_height, &par->con, +- &par->crtc, &init_mode); +- if (ret) +- goto err_kms; +- +- info->var.xres = init_mode->hdisplay; +- info->var.yres = init_mode->vdisplay; +- +- /* +- * Create buffers and alloc memory +- */ +- par->vmalloc = vzalloc(fb_size); +- if (unlikely(par->vmalloc == NULL)) { +- ret = -ENOMEM; +- goto err_free; +- } +- +- /* +- * Fixed and var +- */ +- strcpy(info->fix.id, "svgadrmfb"); +- info->fix.type = FB_TYPE_PACKED_PIXELS; +- info->fix.visual = FB_VISUAL_TRUECOLOR; +- info->fix.type_aux = 0; +- info->fix.xpanstep = 1; /* doing it in hw */ +- info->fix.ypanstep = 1; /* doing it in hw */ +- info->fix.ywrapstep = 0; +- info->fix.accel = FB_ACCEL_NONE; +- info->fix.line_length = fb_pitch; +- +- info->fix.smem_start = 0; +- info->fix.smem_len = fb_size; +- +- info->pseudo_palette = par->pseudo_palette; +- info->screen_base = (char __iomem *)par->vmalloc; +- info->screen_size = fb_size; +- +- info->fbops = &vmw_fb_ops; +- +- /* 24 depth per default */ +- info->var.red.offset = 16; +- info->var.green.offset = 8; +- info->var.blue.offset = 0; +- info->var.red.length = 8; +- info->var.green.length = 8; +- info->var.blue.length = 8; +- info->var.transp.offset = 0; +- info->var.transp.length = 0; +- +- info->var.xres_virtual = fb_width; +- info->var.yres_virtual = fb_height; +- info->var.bits_per_pixel = fb_bpp; +- info->var.xoffset = 0; +- info->var.yoffset = 0; +- info->var.activate = FB_ACTIVATE_NOW; +- info->var.height = -1; +- info->var.width = -1; +- +- /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ +- info->apertures = alloc_apertures(1); +- if (!info->apertures) { +- ret = -ENOMEM; +- goto err_aper; +- } +- info->apertures->ranges[0].base = vmw_priv->vram_start; +- info->apertures->ranges[0].size = vmw_priv->vram_size; +- +- /* +- * Dirty & Deferred IO +- */ +- par->dirty.x1 = par->dirty.x2 = 0; +- par->dirty.y1 = par->dirty.y2 = 0; +- par->dirty.active = true; +- spin_lock_init(&par->dirty.lock); +- mutex_init(&par->bo_mutex); +- info->fbdefio = &vmw_defio; +- fb_deferred_io_init(info); +- +- ret = register_framebuffer(info); +- if (unlikely(ret != 0)) +- goto err_defio; +- +- vmw_fb_set_par(info); +- +- return 0; +- +-err_defio: +- fb_deferred_io_cleanup(info); +-err_aper: +-err_free: +- vfree(par->vmalloc); +-err_kms: +- framebuffer_release(info); +- vmw_priv->fb_info = NULL; +- +- return ret; +-} +- +-int vmw_fb_close(struct vmw_private *vmw_priv) +-{ +- struct fb_info *info; +- struct vmw_fb_par *par; +- +- if (!vmw_priv->fb_info) +- return 0; +- +- info = vmw_priv->fb_info; +- par = info->par; +- +- /* ??? order */ +- fb_deferred_io_cleanup(info); +- cancel_delayed_work_sync(&par->local_work); +- unregister_framebuffer(info); +- +- mutex_lock(&par->bo_mutex); +- (void) vmw_fb_kms_detach(par, true, true); +- mutex_unlock(&par->bo_mutex); +- +- vfree(par->vmalloc); +- framebuffer_release(info); +- +- return 0; +-} +- +-int vmw_fb_off(struct vmw_private *vmw_priv) +-{ +- struct fb_info *info; +- struct vmw_fb_par *par; +- unsigned long flags; +- +- if (!vmw_priv->fb_info) +- return -EINVAL; +- +- info = vmw_priv->fb_info; +- par = info->par; +- +- spin_lock_irqsave(&par->dirty.lock, flags); +- par->dirty.active = false; +- spin_unlock_irqrestore(&par->dirty.lock, flags); +- +- flush_delayed_work(&info->deferred_work); +- flush_delayed_work(&par->local_work); +- +- return 0; +-} +- +-int vmw_fb_on(struct vmw_private *vmw_priv) +-{ +- struct fb_info *info; +- struct vmw_fb_par *par; +- unsigned long flags; +- +- if (!vmw_priv->fb_info) +- return -EINVAL; +- +- info = vmw_priv->fb_info; +- par = info->par; +- +- spin_lock_irqsave(&par->dirty.lock, flags); +- par->dirty.active = true; +- spin_unlock_irqrestore(&par->dirty.lock, flags); +- +- /* +- * Need to reschedule a dirty update, because otherwise that's +- * only done in dirty_mark() if the previous coalesced +- * dirty region was empty. +- */ +- schedule_delayed_work(&par->local_work, 0); +- +- return 0; +-} +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +index b1aed051b41ab..77c6700da0087 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +@@ -1988,6 +1988,8 @@ int vmw_kms_init(struct vmw_private *dev_priv) + dev->mode_config.min_height = 1; + dev->mode_config.max_width = dev_priv->texture_max_width; + dev->mode_config.max_height = dev_priv->texture_max_height; ++ dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32; ++ dev->mode_config.prefer_shadow_fbdev = !dev_priv->has_mob; + + drm_mode_create_suggested_offset_properties(dev); + vmw_kms_create_hotplug_mode_update_property(dev_priv); +@@ -2134,8 +2136,8 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, + du->gui_x = rects[du->unit].x1; + du->gui_y = rects[du->unit].y1; + } else { +- du->pref_width = 800; +- du->pref_height = 600; ++ du->pref_width = VMWGFX_MIN_INITIAL_WIDTH; ++ du->pref_height = VMWGFX_MIN_INITIAL_HEIGHT; + du->pref_active = false; + du->gui_x = 0; + du->gui_y = 0; +@@ -2162,13 +2164,13 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, + } + con->status = vmw_du_connector_detect(con, true); + } +- +- drm_sysfs_hotplug_event(dev); + out_fini: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + mutex_unlock(&dev->mode_config.mutex); + ++ drm_sysfs_hotplug_event(dev); ++ + return 0; + } + +@@ -2448,10 +2450,9 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, + int ret, i; + + if (!arg->num_outputs) { +- struct drm_rect def_rect = {0, 0, 800, 600}; +- VMW_DEBUG_KMS("Default layout x1 = %d y1 = %d x2 = %d y2 = %d\n", +- def_rect.x1, def_rect.y1, +- def_rect.x2, def_rect.y2); ++ struct drm_rect def_rect = {0, 0, ++ VMWGFX_MIN_INITIAL_WIDTH, ++ VMWGFX_MIN_INITIAL_HEIGHT}; + vmw_du_update_layout(dev_priv, 1, &def_rect); + return 0; + } +@@ -2746,68 +2747,6 @@ int vmw_kms_update_proxy(struct vmw_resource *res, + return 0; + } + +-int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, +- unsigned unit, +- u32 max_width, +- u32 max_height, +- struct drm_connector **p_con, +- struct drm_crtc **p_crtc, +- struct drm_display_mode **p_mode) +-{ +- struct drm_connector *con; +- struct vmw_display_unit *du; +- struct drm_display_mode *mode; +- int i = 0; +- int ret = 0; +- +- mutex_lock(&dev_priv->drm.mode_config.mutex); +- list_for_each_entry(con, &dev_priv->drm.mode_config.connector_list, +- head) { +- if (i == unit) +- break; +- +- ++i; +- } +- +- if (&con->head == &dev_priv->drm.mode_config.connector_list) { +- DRM_ERROR("Could not find initial display unit.\n"); +- ret = -EINVAL; +- goto out_unlock; +- } +- +- if (list_empty(&con->modes)) +- (void) vmw_du_connector_fill_modes(con, max_width, max_height); +- +- if (list_empty(&con->modes)) { +- DRM_ERROR("Could not find initial display mode.\n"); +- ret = -EINVAL; +- goto out_unlock; +- } +- +- du = vmw_connector_to_du(con); +- *p_con = con; +- *p_crtc = &du->crtc; +- +- list_for_each_entry(mode, &con->modes, head) { +- if (mode->type & DRM_MODE_TYPE_PREFERRED) +- break; +- } +- +- if (&mode->head == &con->modes) { +- WARN_ONCE(true, "Could not find initial preferred mode.\n"); +- *p_mode = list_first_entry(&con->modes, +- struct drm_display_mode, +- head); +- } else { +- *p_mode = mode; +- } +- +- out_unlock: +- mutex_unlock(&dev_priv->drm.mode_config.mutex); +- +- return ret; +-} +- + /** + * vmw_kms_create_implicit_placement_property - Set up the implicit placement + * property. +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +index b116600b343a8..7e15c851871f2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +@@ -458,13 +458,6 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, + struct vmw_surface *surface, + bool only_2d, + const struct drm_mode_fb_cmd2 *mode_cmd); +-int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, +- unsigned unit, +- u32 max_width, +- u32 max_height, +- struct drm_connector **p_con, +- struct drm_crtc **p_crtc, +- struct drm_display_mode **p_mode); + void vmw_guess_mode_timing(struct drm_display_mode *mode); + void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv); + void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv); +-- +2.43.0 + diff --git a/queue-6.1/drm-vmwgfx-refactor-drm-connector-probing-for-displa.patch b/queue-6.1/drm-vmwgfx-refactor-drm-connector-probing-for-displa.patch new file mode 100644 index 00000000000..e0c75123bf7 --- /dev/null +++ b/queue-6.1/drm-vmwgfx-refactor-drm-connector-probing-for-displa.patch @@ -0,0 +1,445 @@ +From 695b16d84a82d773c1257b50ea80957ace4e6fff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jan 2024 15:08:00 -0500 +Subject: drm/vmwgfx: Refactor drm connector probing for display modes + +From: Martin Krastev + +[ Upstream commit 935f795045a6f9b13d28d46ebdad04bfea8750dd ] + +Implement drm_connector_helper_funcs.mode_valid and .get_modes, +replacing custom drm_connector_funcs.fill_modes code with +drm_helper_probe_single_connector_modes; for STDU, LDU & SOU +display units. + +Signed-off-by: Martin Krastev +Reviewed-by: Zack Rusin +Signed-off-by: Zack Rusin +Link: https://patchwork.freedesktop.org/patch/msgid/20240126200804.732454-2-zack.rusin@broadcom.com +Stable-dep-of: 426826933109 ("drm/vmwgfx: Filter modes which exceed graphics memory") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 272 +++++++++------------------ + drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 6 +- + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 5 +- + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 5 +- + drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 4 +- + 5 files changed, 101 insertions(+), 191 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +index 77c6700da0087..7a2d29370a534 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include "vmwgfx_kms.h" + +@@ -2213,107 +2214,6 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force) + connector_status_connected : connector_status_disconnected); + } + +-static struct drm_display_mode vmw_kms_connector_builtin[] = { +- /* 640x480@60Hz */ +- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, +- 752, 800, 0, 480, 489, 492, 525, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 800x600@60Hz */ +- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, +- 968, 1056, 0, 600, 601, 605, 628, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1024x768@60Hz */ +- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, +- 1184, 1344, 0, 768, 771, 777, 806, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 1152x864@75Hz */ +- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, +- 1344, 1600, 0, 864, 865, 868, 900, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1280x720@60Hz */ +- { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74500, 1280, 1344, +- 1472, 1664, 0, 720, 723, 728, 748, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1280x768@60Hz */ +- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, +- 1472, 1664, 0, 768, 771, 778, 798, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1280x800@60Hz */ +- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, +- 1480, 1680, 0, 800, 803, 809, 831, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 1280x960@60Hz */ +- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, +- 1488, 1800, 0, 960, 961, 964, 1000, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1280x1024@60Hz */ +- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, +- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1360x768@60Hz */ +- { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, +- 1536, 1792, 0, 768, 771, 777, 795, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1440x1050@60Hz */ +- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, +- 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1440x900@60Hz */ +- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, +- 1672, 1904, 0, 900, 903, 909, 934, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1600x1200@60Hz */ +- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, +- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1680x1050@60Hz */ +- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, +- 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1792x1344@60Hz */ +- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, +- 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1853x1392@60Hz */ +- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, +- 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1920x1080@60Hz */ +- { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 173000, 1920, 2048, +- 2248, 2576, 0, 1080, 1083, 1088, 1120, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1920x1200@60Hz */ +- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, +- 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 1920x1440@60Hz */ +- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, +- 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 2560x1440@60Hz */ +- { DRM_MODE("2560x1440", DRM_MODE_TYPE_DRIVER, 241500, 2560, 2608, +- 2640, 2720, 0, 1440, 1443, 1448, 1481, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 2560x1600@60Hz */ +- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, +- 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, +- /* 2880x1800@60Hz */ +- { DRM_MODE("2880x1800", DRM_MODE_TYPE_DRIVER, 337500, 2880, 2928, +- 2960, 3040, 0, 1800, 1803, 1809, 1852, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 3840x2160@60Hz */ +- { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 533000, 3840, 3888, +- 3920, 4000, 0, 2160, 2163, 2168, 2222, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* 3840x2400@60Hz */ +- { DRM_MODE("3840x2400", DRM_MODE_TYPE_DRIVER, 592250, 3840, 3888, +- 3920, 4000, 0, 2400, 2403, 2409, 2469, 0, +- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, +- /* Terminate */ +- { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, +-}; +- + /** + * vmw_guess_mode_timing - Provide fake timings for a + * 60Hz vrefresh mode. +@@ -2335,88 +2235,6 @@ void vmw_guess_mode_timing(struct drm_display_mode *mode) + } + + +-int vmw_du_connector_fill_modes(struct drm_connector *connector, +- uint32_t max_width, uint32_t max_height) +-{ +- struct vmw_display_unit *du = vmw_connector_to_du(connector); +- struct drm_device *dev = connector->dev; +- struct vmw_private *dev_priv = vmw_priv(dev); +- struct drm_display_mode *mode = NULL; +- struct drm_display_mode *bmode; +- struct drm_display_mode prefmode = { DRM_MODE("preferred", +- DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) +- }; +- int i; +- u32 assumed_bpp = 4; +- +- if (dev_priv->assume_16bpp) +- assumed_bpp = 2; +- +- max_width = min(max_width, dev_priv->texture_max_width); +- max_height = min(max_height, dev_priv->texture_max_height); +- +- /* +- * For STDU extra limit for a mode on SVGA_REG_SCREENTARGET_MAX_WIDTH/ +- * HEIGHT registers. +- */ +- if (dev_priv->active_display_unit == vmw_du_screen_target) { +- max_width = min(max_width, dev_priv->stdu_max_width); +- max_height = min(max_height, dev_priv->stdu_max_height); +- } +- +- /* Add preferred mode */ +- mode = drm_mode_duplicate(dev, &prefmode); +- if (!mode) +- return 0; +- mode->hdisplay = du->pref_width; +- mode->vdisplay = du->pref_height; +- vmw_guess_mode_timing(mode); +- drm_mode_set_name(mode); +- +- if (vmw_kms_validate_mode_vram(dev_priv, +- mode->hdisplay * assumed_bpp, +- mode->vdisplay)) { +- drm_mode_probed_add(connector, mode); +- } else { +- drm_mode_destroy(dev, mode); +- mode = NULL; +- } +- +- if (du->pref_mode) { +- list_del_init(&du->pref_mode->head); +- drm_mode_destroy(dev, du->pref_mode); +- } +- +- /* mode might be null here, this is intended */ +- du->pref_mode = mode; +- +- for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { +- bmode = &vmw_kms_connector_builtin[i]; +- if (bmode->hdisplay > max_width || +- bmode->vdisplay > max_height) +- continue; +- +- if (!vmw_kms_validate_mode_vram(dev_priv, +- bmode->hdisplay * assumed_bpp, +- bmode->vdisplay)) +- continue; +- +- mode = drm_mode_duplicate(dev, bmode); +- if (!mode) +- return 0; +- +- drm_mode_probed_add(connector, mode); +- } +- +- drm_connector_list_update(connector); +- /* Move the prefered mode first, help apps pick the right mode. */ +- drm_mode_sort(&connector->modes); +- +- return 1; +-} +- + /** + * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl + * @dev: drm device for the ioctl +@@ -2945,3 +2763,91 @@ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update) + vmw_validation_unref_lists(&val_ctx); + return ret; + } ++ ++/** ++ * vmw_connector_mode_valid - implements drm_connector_helper_funcs.mode_valid callback ++ * ++ * @connector: the drm connector, part of a DU container ++ * @mode: drm mode to check ++ * ++ * Returns MODE_OK on success, or a drm_mode_status error code. ++ */ ++enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ struct drm_device *dev = connector->dev; ++ struct vmw_private *dev_priv = vmw_priv(dev); ++ u32 max_width = dev_priv->texture_max_width; ++ u32 max_height = dev_priv->texture_max_height; ++ u32 assumed_cpp = 4; ++ ++ if (dev_priv->assume_16bpp) ++ assumed_cpp = 2; ++ ++ if (dev_priv->active_display_unit == vmw_du_screen_target) { ++ max_width = min(dev_priv->stdu_max_width, max_width); ++ max_height = min(dev_priv->stdu_max_height, max_height); ++ } ++ ++ if (max_width < mode->hdisplay) ++ return MODE_BAD_HVALUE; ++ ++ if (max_height < mode->vdisplay) ++ return MODE_BAD_VVALUE; ++ ++ if (!vmw_kms_validate_mode_vram(dev_priv, ++ mode->hdisplay * assumed_cpp, ++ mode->vdisplay)) ++ return MODE_MEM; ++ ++ return MODE_OK; ++} ++ ++/** ++ * vmw_connector_get_modes - implements drm_connector_helper_funcs.get_modes callback ++ * ++ * @connector: the drm connector, part of a DU container ++ * ++ * Returns the number of added modes. ++ */ ++int vmw_connector_get_modes(struct drm_connector *connector) ++{ ++ struct vmw_display_unit *du = vmw_connector_to_du(connector); ++ struct drm_device *dev = connector->dev; ++ struct vmw_private *dev_priv = vmw_priv(dev); ++ struct drm_display_mode *mode = NULL; ++ struct drm_display_mode prefmode = { DRM_MODE("preferred", ++ DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) ++ }; ++ u32 max_width; ++ u32 max_height; ++ u32 num_modes; ++ ++ /* Add preferred mode */ ++ mode = drm_mode_duplicate(dev, &prefmode); ++ if (!mode) ++ return 0; ++ ++ mode->hdisplay = du->pref_width; ++ mode->vdisplay = du->pref_height; ++ vmw_guess_mode_timing(mode); ++ drm_mode_set_name(mode); ++ ++ drm_mode_probed_add(connector, mode); ++ drm_dbg_kms(dev, "preferred mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); ++ ++ /* Probe connector for all modes not exceeding our geom limits */ ++ max_width = dev_priv->texture_max_width; ++ max_height = dev_priv->texture_max_height; ++ ++ if (dev_priv->active_display_unit == vmw_du_screen_target) { ++ max_width = min(dev_priv->stdu_max_width, max_width); ++ max_height = min(dev_priv->stdu_max_height, max_height); ++ } ++ ++ num_modes = 1 + drm_add_modes_noedid(connector, max_width, max_height); ++ ++ return num_modes; ++} +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +index 7e15c851871f2..1099de1ece4b3 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +@@ -379,7 +379,6 @@ struct vmw_display_unit { + unsigned pref_width; + unsigned pref_height; + bool pref_active; +- struct drm_display_mode *pref_mode; + + /* + * Gui positioning +@@ -429,8 +428,6 @@ void vmw_du_connector_save(struct drm_connector *connector); + void vmw_du_connector_restore(struct drm_connector *connector); + enum drm_connector_status + vmw_du_connector_detect(struct drm_connector *connector, bool force); +-int vmw_du_connector_fill_modes(struct drm_connector *connector, +- uint32_t max_width, uint32_t max_height); + int vmw_kms_helper_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + const struct drm_clip_rect *clips, +@@ -439,6 +436,9 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, + int num_clips, + int increment, + struct vmw_kms_dirty *dirty); ++enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, ++ struct drm_display_mode *mode); ++int vmw_connector_get_modes(struct drm_connector *connector); + + void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, + struct drm_file *file_priv, +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +index ac72c20715f32..fdaf7d28cb211 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +@@ -263,7 +263,7 @@ static void vmw_ldu_connector_destroy(struct drm_connector *connector) + static const struct drm_connector_funcs vmw_legacy_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, +- .fill_modes = vmw_du_connector_fill_modes, ++ .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vmw_ldu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, +@@ -272,6 +272,8 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { + + static const struct + drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { ++ .get_modes = vmw_connector_get_modes, ++ .mode_valid = vmw_connector_mode_valid + }; + + static int vmw_kms_ldu_do_bo_dirty(struct vmw_private *dev_priv, +@@ -408,7 +410,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) + ldu->base.pref_active = (unit == 0); + ldu->base.pref_width = dev_priv->initial_width; + ldu->base.pref_height = dev_priv->initial_height; +- ldu->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +index e1f36a09c59c1..e33684f56eda8 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +@@ -346,7 +346,7 @@ static void vmw_sou_connector_destroy(struct drm_connector *connector) + static const struct drm_connector_funcs vmw_sou_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, +- .fill_modes = vmw_du_connector_fill_modes, ++ .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vmw_sou_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, +@@ -356,6 +356,8 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { + + static const struct + drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { ++ .get_modes = vmw_connector_get_modes, ++ .mode_valid = vmw_connector_mode_valid + }; + + +@@ -827,7 +829,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) + sou->base.pref_active = (unit == 0); + sou->base.pref_width = dev_priv->initial_width; + sou->base.pref_height = dev_priv->initial_height; +- sou->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +index 0090abe892548..b3e70ace6d9b0 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +@@ -977,7 +977,7 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector) + static const struct drm_connector_funcs vmw_stdu_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, +- .fill_modes = vmw_du_connector_fill_modes, ++ .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vmw_stdu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, +@@ -987,6 +987,8 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { + + static const struct + drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { ++ .get_modes = vmw_connector_get_modes, ++ .mode_valid = vmw_connector_mode_valid + }; + + +-- +2.43.0 + diff --git a/queue-6.1/drm-vmwgfx-remove-stdu-logic-from-generic-mode_valid.patch b/queue-6.1/drm-vmwgfx-remove-stdu-logic-from-generic-mode_valid.patch new file mode 100644 index 00000000000..f6586f0e49a --- /dev/null +++ b/queue-6.1/drm-vmwgfx-remove-stdu-logic-from-generic-mode_valid.patch @@ -0,0 +1,93 @@ +From 08d024c4be883d839b051471aae1e3b7e50512ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 May 2024 13:47:19 -0500 +Subject: drm/vmwgfx: Remove STDU logic from generic mode_valid function + +From: Ian Forbes + +[ Upstream commit dde1de06bd7248fd83c4ce5cf0dbe9e4e95bbb91 ] + +STDU has its own mode_valid function now so this logic can be removed from +the generic version. + +Fixes: 935f795045a6 ("drm/vmwgfx: Refactor drm connector probing for display modes") + +Signed-off-by: Ian Forbes +Signed-off-by: Zack Rusin +Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-4-ian.forbes@broadcom.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 3 --- + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 26 +++++++++----------------- + 2 files changed, 9 insertions(+), 20 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index 00d9e58b7e149..b0c23559511a1 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -1194,9 +1194,6 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, + int vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bpp, unsigned depth); +-bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, +- uint32_t pitch, +- uint32_t height); + int vmw_kms_present(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +index 7a2d29370a534..5b30e4ba2811a 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +@@ -2085,13 +2085,12 @@ int vmw_kms_write_svga(struct vmw_private *vmw_priv, + return 0; + } + ++static + bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, +- uint32_t pitch, +- uint32_t height) ++ u64 pitch, ++ u64 height) + { +- return ((u64) pitch * (u64) height) < (u64) +- ((dev_priv->active_display_unit == vmw_du_screen_target) ? +- dev_priv->max_primary_mem : dev_priv->vram_size); ++ return (pitch * height) < (u64)dev_priv->vram_size; + } + + /** +@@ -2775,25 +2774,18 @@ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update) + enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { ++ enum drm_mode_status ret; + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); +- u32 max_width = dev_priv->texture_max_width; +- u32 max_height = dev_priv->texture_max_height; + u32 assumed_cpp = 4; + + if (dev_priv->assume_16bpp) + assumed_cpp = 2; + +- if (dev_priv->active_display_unit == vmw_du_screen_target) { +- max_width = min(dev_priv->stdu_max_width, max_width); +- max_height = min(dev_priv->stdu_max_height, max_height); +- } +- +- if (max_width < mode->hdisplay) +- return MODE_BAD_HVALUE; +- +- if (max_height < mode->vdisplay) +- return MODE_BAD_VVALUE; ++ ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, ++ dev_priv->texture_max_height); ++ if (ret != MODE_OK) ++ return ret; + + if (!vmw_kms_validate_mode_vram(dev_priv, + mode->hdisplay * assumed_cpp, +-- +2.43.0 + diff --git a/queue-6.1/geneve-fix-incorrect-inner-network-header-offset-whe.patch b/queue-6.1/geneve-fix-incorrect-inner-network-header-offset-whe.patch new file mode 100644 index 00000000000..ccaf99e60c5 --- /dev/null +++ b/queue-6.1/geneve-fix-incorrect-inner-network-header-offset-whe.patch @@ -0,0 +1,106 @@ +From c413cb476ab32d7b022a4abbad82c80c068e3e19 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Jun 2024 23:32:48 +0300 +Subject: geneve: Fix incorrect inner network header offset when + innerprotoinherit is set + +From: Gal Pressman + +[ Upstream commit c6ae073f5903f6c6439d0ac855836a4da5c0a701 ] + +When innerprotoinherit is set, the tunneled packets do not have an inner +Ethernet header. +Change 'maclen' to not always assume the header length is ETH_HLEN, as +there might not be a MAC header. + +This resolves issues with drivers (e.g. mlx5, in +mlx5e_tx_tunnel_accel()) who rely on the skb inner network header offset +to be correct, and use it for TX offloads. + +Fixes: d8a6213d70ac ("geneve: fix header validation in geneve[6]_xmit_skb") +Signed-off-by: Gal Pressman +Signed-off-by: Tariq Toukan +Reviewed-by: Wojciech Drewek +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/geneve.c | 10 ++++++---- + include/net/ip_tunnels.h | 5 +++-- + 2 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c +index 488ca1c854962..c4a49a75250e3 100644 +--- a/drivers/net/geneve.c ++++ b/drivers/net/geneve.c +@@ -919,6 +919,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct geneve_dev *geneve, + const struct ip_tunnel_info *info) + { ++ bool inner_proto_inherit = geneve->cfg.inner_proto_inherit; + bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); + const struct ip_tunnel_key *key = &info->key; +@@ -930,7 +931,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, + __be16 sport; + int err; + +- if (!skb_vlan_inet_prepare(skb)) ++ if (!skb_vlan_inet_prepare(skb, inner_proto_inherit)) + return -EINVAL; + + sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); +@@ -1003,7 +1004,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, + } + + err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr), +- geneve->cfg.inner_proto_inherit); ++ inner_proto_inherit); + if (unlikely(err)) + return err; + +@@ -1019,6 +1020,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct geneve_dev *geneve, + const struct ip_tunnel_info *info) + { ++ bool inner_proto_inherit = geneve->cfg.inner_proto_inherit; + bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); + const struct ip_tunnel_key *key = &info->key; +@@ -1028,7 +1030,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, + __be16 sport; + int err; + +- if (!skb_vlan_inet_prepare(skb)) ++ if (!skb_vlan_inet_prepare(skb, inner_proto_inherit)) + return -EINVAL; + + sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); +@@ -1083,7 +1085,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, + ttl = ttl ? : ip6_dst_hoplimit(dst); + } + err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr), +- geneve->cfg.inner_proto_inherit); ++ inner_proto_inherit); + if (unlikely(err)) + return err; + +diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h +index f9906b73e7ff4..0cc077c3dda30 100644 +--- a/include/net/ip_tunnels.h ++++ b/include/net/ip_tunnels.h +@@ -353,9 +353,10 @@ static inline bool pskb_inet_may_pull(struct sk_buff *skb) + + /* Variant of pskb_inet_may_pull(). + */ +-static inline bool skb_vlan_inet_prepare(struct sk_buff *skb) ++static inline bool skb_vlan_inet_prepare(struct sk_buff *skb, ++ bool inner_proto_inherit) + { +- int nhlen = 0, maclen = ETH_HLEN; ++ int nhlen = 0, maclen = inner_proto_inherit ? 0 : ETH_HLEN; + __be16 type = skb->protocol; + + /* Essentially this is skb_protocol(skb, true) +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-convert-to-immutable-irq_chip.patch b/queue-6.1/gpio-tqmx86-convert-to-immutable-irq_chip.patch new file mode 100644 index 00000000000..4ea789b9bf5 --- /dev/null +++ b/queue-6.1/gpio-tqmx86-convert-to-immutable-irq_chip.patch @@ -0,0 +1,108 @@ +From eff14fc5f545f83b3d33f3cbd40117ec03fc5e2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Mar 2023 10:55:12 +0100 +Subject: gpio: tqmx86: Convert to immutable irq_chip + +From: Linus Walleij + +[ Upstream commit 8e43827b6ae727a745ce7a8cc19184b28905a965 ] + +Convert the driver to immutable irq-chip with a bit of +intuition. + +Cc: Marc Zyngier +Signed-off-by: Linus Walleij +Reviewed-by: Marc Zyngier +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 08af509efdf8 ("gpio: tqmx86: store IRQ trigger type and unmask status separately") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-tqmx86.c | 28 ++++++++++++++++++++-------- + 1 file changed, 20 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c +index da689b5b3fad2..b7e2dbbdc4ebe 100644 +--- a/drivers/gpio/gpio-tqmx86.c ++++ b/drivers/gpio/gpio-tqmx86.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + + #define TQMX86_NGPIO 8 +@@ -35,7 +36,6 @@ + + struct tqmx86_gpio_data { + struct gpio_chip chip; +- struct irq_chip irq_chip; + void __iomem *io_base; + int irq; + raw_spinlock_t spinlock; +@@ -119,6 +119,7 @@ static void tqmx86_gpio_irq_mask(struct irq_data *data) + gpiic &= ~mask; + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); ++ gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); + } + + static void tqmx86_gpio_irq_unmask(struct irq_data *data) +@@ -131,6 +132,7 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + ++ gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; +@@ -223,6 +225,22 @@ static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip, + clear_bit(3, valid_mask); + } + ++static void tqmx86_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(d); ++ ++ seq_printf(p, gc->label); ++} ++ ++static const struct irq_chip tqmx86_gpio_irq_chip = { ++ .irq_mask = tqmx86_gpio_irq_mask, ++ .irq_unmask = tqmx86_gpio_irq_unmask, ++ .irq_set_type = tqmx86_gpio_irq_set_type, ++ .irq_print_chip = tqmx86_gpio_irq_print_chip, ++ .flags = IRQCHIP_IMMUTABLE, ++ GPIOCHIP_IRQ_RESOURCE_HELPERS, ++}; ++ + static int tqmx86_gpio_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -279,14 +297,8 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) + pm_runtime_enable(&pdev->dev); + + if (irq > 0) { +- struct irq_chip *irq_chip = &gpio->irq_chip; + u8 irq_status; + +- irq_chip->name = chip->label; +- irq_chip->irq_mask = tqmx86_gpio_irq_mask; +- irq_chip->irq_unmask = tqmx86_gpio_irq_unmask; +- irq_chip->irq_set_type = tqmx86_gpio_irq_set_type; +- + /* Mask all interrupts */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC); + +@@ -295,7 +307,7 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + girq = &chip->irq; +- girq->chip = irq_chip; ++ gpio_irq_chip_set_chip(girq, &tqmx86_gpio_irq_chip); + girq->parent_handler = tqmx86_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-fix-broken-irq_type_edge_both-interrupt-.patch b/queue-6.1/gpio-tqmx86-fix-broken-irq_type_edge_both-interrupt-.patch new file mode 100644 index 00000000000..5902064d0c4 --- /dev/null +++ b/queue-6.1/gpio-tqmx86-fix-broken-irq_type_edge_both-interrupt-.patch @@ -0,0 +1,114 @@ +From e6a70c4a2544d6ed34522ddcdb6315d366385972 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 May 2024 12:20:02 +0200 +Subject: gpio: tqmx86: fix broken IRQ_TYPE_EDGE_BOTH interrupt type + +From: Matthias Schiffer + +[ Upstream commit 90dd7de4ef7ba584823dfbeba834c2919a4bb55b ] + +The TQMx86 GPIO controller only supports falling and rising edge +triggers, but not both. Fix this by implementing a software both-edge +mode that toggles the edge type after every interrupt. + +Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") +Co-developed-by: Gregor Herburger +Signed-off-by: Gregor Herburger +Signed-off-by: Matthias Schiffer +Link: https://lore.kernel.org/r/515324f0491c4d44f4ef49f170354aca002d81ef.1717063994.git.matthias.schiffer@ew.tq-group.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-tqmx86.c | 46 ++++++++++++++++++++++++++++++++++---- + 1 file changed, 42 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c +index 7e428c872a257..f2e7e8754d95d 100644 +--- a/drivers/gpio/gpio-tqmx86.c ++++ b/drivers/gpio/gpio-tqmx86.c +@@ -32,6 +32,10 @@ + #define TQMX86_GPII_NONE 0 + #define TQMX86_GPII_FALLING BIT(0) + #define TQMX86_GPII_RISING BIT(1) ++/* Stored in irq_type as a trigger type, but not actually valid as a register ++ * value, so the name doesn't use "GPII" ++ */ ++#define TQMX86_INT_BOTH (BIT(0) | BIT(1)) + #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) + #define TQMX86_GPII_BITS 2 + /* Stored in irq_type with GPII bits */ +@@ -113,9 +117,15 @@ static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) + { + u8 type = TQMX86_GPII_NONE, gpiic; + +- if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) ++ if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { + type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + ++ if (type == TQMX86_INT_BOTH) ++ type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) ++ ? TQMX86_GPII_FALLING ++ : TQMX86_GPII_RISING; ++ } ++ + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); + gpiic |= type << (offset * TQMX86_GPII_BITS); +@@ -169,7 +179,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) + new_type = TQMX86_GPII_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: +- new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; ++ new_type = TQMX86_INT_BOTH; + break; + default: + return -EINVAL; /* not supported */ +@@ -189,8 +199,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); +- unsigned long irq_bits; +- int i = 0; ++ unsigned long irq_bits, flags; ++ int i; + u8 irq_status; + + chained_irq_enter(irq_chip, desc); +@@ -199,6 +209,34 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + irq_bits = irq_status; ++ ++ raw_spin_lock_irqsave(&gpio->spinlock, flags); ++ for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { ++ /* ++ * Edge-both triggers are implemented by flipping the edge ++ * trigger after each interrupt, as the controller only supports ++ * either rising or falling edge triggers, but not both. ++ * ++ * Internally, the TQMx86 GPIO controller has separate status ++ * registers for rising and falling edge interrupts. GPIIC ++ * configures which bits from which register are visible in the ++ * interrupt status register GPIIS and defines what triggers the ++ * parent IRQ line. Writing to GPIIS always clears both rising ++ * and falling interrupt flags internally, regardless of the ++ * currently configured trigger. ++ * ++ * In consequence, we can cleanly implement the edge-both ++ * trigger in software by first clearing the interrupt and then ++ * setting the new trigger based on the current GPIO input in ++ * tqmx86_gpio_irq_config() - even if an edge arrives between ++ * reading the input and setting the trigger, we will have a new ++ * interrupt pending. ++ */ ++ if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) ++ tqmx86_gpio_irq_config(gpio, i); ++ } ++ raw_spin_unlock_irqrestore(&gpio->spinlock, flags); ++ + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) + generic_handle_domain_irq(gpio->chip.irq.domain, + i + TQMX86_NGPO); +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-fix-typo-in-kconfig-label.patch b/queue-6.1/gpio-tqmx86-fix-typo-in-kconfig-label.patch new file mode 100644 index 00000000000..29d6e22cd6e --- /dev/null +++ b/queue-6.1/gpio-tqmx86-fix-typo-in-kconfig-label.patch @@ -0,0 +1,38 @@ +From 5949da5f653b399a4267e448be6add9ac97c066a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 May 2024 12:19:59 +0200 +Subject: gpio: tqmx86: fix typo in Kconfig label + +From: Gregor Herburger + +[ Upstream commit 8c219e52ca4d9a67cd6a7074e91bf29b55edc075 ] + +Fix description for GPIO_TQMX86 from QTMX86 to TQMx86. + +Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") +Signed-off-by: Gregor Herburger +Signed-off-by: Matthias Schiffer +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/e0e38c9944ad6d281d9a662a45d289b88edc808e.1717063994.git.matthias.schiffer@ew.tq-group.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 700f71c954956..b23ef29f56020 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1416,7 +1416,7 @@ config GPIO_TPS68470 + are "output only" GPIOs. + + config GPIO_TQMX86 +- tristate "TQ-Systems QTMX86 GPIO" ++ tristate "TQ-Systems TQMx86 GPIO" + depends on MFD_TQMX86 || COMPILE_TEST + depends on HAS_IOPORT_MAP + select GPIOLIB_IRQCHIP +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-introduce-shadow-register-for-gpio-outpu.patch b/queue-6.1/gpio-tqmx86-introduce-shadow-register-for-gpio-outpu.patch new file mode 100644 index 00000000000..ab23fc27990 --- /dev/null +++ b/queue-6.1/gpio-tqmx86-introduce-shadow-register-for-gpio-outpu.patch @@ -0,0 +1,89 @@ +From 692a3adb85bcc1466919f572da79afa725725a7e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 May 2024 12:20:00 +0200 +Subject: gpio: tqmx86: introduce shadow register for GPIO output value + +From: Matthias Schiffer + +[ Upstream commit 9d6a811b522ba558bcb4ec01d12e72a0af8e9f6e ] + +The TQMx86 GPIO controller uses the same register address for input and +output data. Reading the register will always return current inputs +rather than the previously set outputs (regardless of the current +direction setting). Therefore, using a RMW pattern does not make sense +when setting output values. Instead, the previously set output register +value needs to be stored as a shadow register. + +As there is no reliable way to get the current output values from the +hardware, also initialize all channels to 0, to ensure that stored and +actual output values match. This should usually not have any effect in +practise, as the TQMx86 UEFI sets all outputs to 0 during boot. + +Also prepare for extension of the driver to more than 8 GPIOs by using +DECLARE_BITMAP. + +Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") +Signed-off-by: Matthias Schiffer +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/d0555933becd45fa92a85675d26e4d59343ddc01.1717063994.git.matthias.schiffer@ew.tq-group.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-tqmx86.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c +index f0a2cf4b06796..da689b5b3fad2 100644 +--- a/drivers/gpio/gpio-tqmx86.c ++++ b/drivers/gpio/gpio-tqmx86.c +@@ -6,6 +6,7 @@ + * Vadim V.Vlasov + */ + ++#include + #include + #include + #include +@@ -38,6 +39,7 @@ struct tqmx86_gpio_data { + void __iomem *io_base; + int irq; + raw_spinlock_t spinlock; ++ DECLARE_BITMAP(output, TQMX86_NGPIO); + u8 irq_type[TQMX86_NGPI]; + }; + +@@ -64,15 +66,10 @@ static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + { + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + unsigned long flags; +- u8 val; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); +- val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); +- if (value) +- val |= BIT(offset); +- else +- val &= ~BIT(offset); +- tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); ++ __assign_bit(offset, gpio->output, value); ++ tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + } + +@@ -259,6 +256,13 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) + + tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + ++ /* ++ * Reading the previous output state is not possible with TQMx86 hardware. ++ * Initialize all outputs to 0 to have a defined state that matches the ++ * shadow register. ++ */ ++ tqmx86_gpio_write(gpio, 0, TQMX86_GPIOD); ++ + chip = &gpio->chip; + chip->label = "gpio-tqmx86"; + chip->owner = THIS_MODULE; +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-remove-unneeded-call-to-platform_set_drv.patch b/queue-6.1/gpio-tqmx86-remove-unneeded-call-to-platform_set_drv.patch new file mode 100644 index 00000000000..49a82f33000 --- /dev/null +++ b/queue-6.1/gpio-tqmx86-remove-unneeded-call-to-platform_set_drv.patch @@ -0,0 +1,40 @@ +From d85ec7f519b347a06f19f93144a373b8d7efab42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 1 Aug 2023 23:38:39 +0300 +Subject: gpio: tqmx86: remove unneeded call to platform_set_drvdata() + +From: Andrei Coardos + +[ Upstream commit 0a5e9306b812fe3517548fab92b3d3d6ce7576e5 ] + +This function call was found to be unnecessary as there is no equivalent +platform_get_drvdata() call to access the private data of the driver. Also, +the private data is defined in this driver, so there is no risk of it being +accessed outside of this driver file. + +Reviewed-by: Alexandru Ardelean +Signed-off-by: Andrei Coardos +Reviewed-by: Andy Shevchenko +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 9d6a811b522b ("gpio: tqmx86: introduce shadow register for GPIO output value") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-tqmx86.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c +index e739dcea61b23..f0a2cf4b06796 100644 +--- a/drivers/gpio/gpio-tqmx86.c ++++ b/drivers/gpio/gpio-tqmx86.c +@@ -259,8 +259,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) + + tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + +- platform_set_drvdata(pdev, gpio); +- + chip = &gpio->chip; + chip->label = "gpio-tqmx86"; + chip->owner = THIS_MODULE; +-- +2.43.0 + diff --git a/queue-6.1/gpio-tqmx86-store-irq-trigger-type-and-unmask-status.patch b/queue-6.1/gpio-tqmx86-store-irq-trigger-type-and-unmask-status.patch new file mode 100644 index 00000000000..ab96fa857d7 --- /dev/null +++ b/queue-6.1/gpio-tqmx86-store-irq-trigger-type-and-unmask-status.patch @@ -0,0 +1,137 @@ +From e8bdb9754b7ff1086390e6258dabf840bb31afb3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 May 2024 12:20:01 +0200 +Subject: gpio: tqmx86: store IRQ trigger type and unmask status separately + +From: Matthias Schiffer + +[ Upstream commit 08af509efdf8dad08e972b48de0e2c2a7919ea8b ] + +irq_set_type() should not implicitly unmask the IRQ. + +All accesses to the interrupt configuration register are moved to a new +helper tqmx86_gpio_irq_config(). We also introduce the new rule that +accessing irq_type must happen while locked, which will become +significant for fixing EDGE_BOTH handling. + +Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") +Signed-off-by: Matthias Schiffer +Link: https://lore.kernel.org/r/6aa4f207f77cb58ef64ffb947e91949b0f753ccd.1717063994.git.matthias.schiffer@ew.tq-group.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-tqmx86.c | 48 ++++++++++++++++++++++---------------- + 1 file changed, 28 insertions(+), 20 deletions(-) + +diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c +index b7e2dbbdc4ebe..7e428c872a257 100644 +--- a/drivers/gpio/gpio-tqmx86.c ++++ b/drivers/gpio/gpio-tqmx86.c +@@ -29,15 +29,19 @@ + #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ + #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ + ++#define TQMX86_GPII_NONE 0 + #define TQMX86_GPII_FALLING BIT(0) + #define TQMX86_GPII_RISING BIT(1) + #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) + #define TQMX86_GPII_BITS 2 ++/* Stored in irq_type with GPII bits */ ++#define TQMX86_INT_UNMASKED BIT(2) + + struct tqmx86_gpio_data { + struct gpio_chip chip; + void __iomem *io_base; + int irq; ++ /* Lock must be held for accessing output and irq_type fields */ + raw_spinlock_t spinlock; + DECLARE_BITMAP(output, TQMX86_NGPIO); + u8 irq_type[TQMX86_NGPI]; +@@ -104,21 +108,32 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, + return GPIO_LINE_DIRECTION_OUT; + } + ++static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) ++ __must_hold(&gpio->spinlock) ++{ ++ u8 type = TQMX86_GPII_NONE, gpiic; ++ ++ if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) ++ type = gpio->irq_type[offset] & TQMX86_GPII_MASK; ++ ++ gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); ++ gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); ++ gpiic |= type << (offset * TQMX86_GPII_BITS); ++ tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); ++} ++ + static void tqmx86_gpio_irq_mask(struct irq_data *data) + { + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; +- u8 gpiic, mask; +- +- mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); +- gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); +- gpiic &= ~mask; +- tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); ++ gpio->irq_type[offset] &= ~TQMX86_INT_UNMASKED; ++ tqmx86_gpio_irq_config(gpio, offset); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); ++ + gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); + } + +@@ -128,16 +143,12 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; +- u8 gpiic, mask; +- +- mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); ++ + raw_spin_lock_irqsave(&gpio->spinlock, flags); +- gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); +- gpiic &= ~mask; +- gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); +- tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); ++ gpio->irq_type[offset] |= TQMX86_INT_UNMASKED; ++ tqmx86_gpio_irq_config(gpio, offset); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + } + +@@ -148,7 +159,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) + unsigned int offset = (data->hwirq - TQMX86_NGPO); + unsigned int edge_type = type & IRQF_TRIGGER_MASK; + unsigned long flags; +- u8 new_type, gpiic; ++ u8 new_type; + + switch (edge_type) { + case IRQ_TYPE_EDGE_RISING: +@@ -164,13 +175,10 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) + return -EINVAL; /* not supported */ + } + +- gpio->irq_type[offset] = new_type; +- + raw_spin_lock_irqsave(&gpio->spinlock, flags); +- gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); +- gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); +- gpiic |= new_type << (offset * TQMX86_GPII_BITS); +- tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); ++ gpio->irq_type[offset] &= ~TQMX86_GPII_MASK; ++ gpio->irq_type[offset] |= new_type; ++ tqmx86_gpio_irq_config(gpio, offset); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + + return 0; +-- +2.43.0 + diff --git a/queue-6.1/gve-ignore-nonrelevant-gso-type-bits-when-processing.patch b/queue-6.1/gve-ignore-nonrelevant-gso-type-bits-when-processing.patch new file mode 100644 index 00000000000..4d700fb6d6d --- /dev/null +++ b/queue-6.1/gve-ignore-nonrelevant-gso-type-bits-when-processing.patch @@ -0,0 +1,86 @@ +From 9de3955b30f638a11671c90bef872414ae790407 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 10 Jun 2024 15:57:18 -0700 +Subject: gve: ignore nonrelevant GSO type bits when processing TSO headers + +From: Joshua Washington + +[ Upstream commit 1b9f756344416e02b41439bf2324b26aa25e141c ] + +TSO currently fails when the skb's gso_type field has more than one bit +set. + +TSO packets can be passed from userspace using PF_PACKET, TUNTAP and a +few others, using virtio_net_hdr (e.g., PACKET_VNET_HDR). This includes +virtualization, such as QEMU, a real use-case. + +The gso_type and gso_size fields as passed from userspace in +virtio_net_hdr are not trusted blindly by the kernel. It adds gso_type +|= SKB_GSO_DODGY to force the packet to enter the software GSO stack +for verification. + +This issue might similarly come up when the CWR bit is set in the TCP +header for congestion control, causing the SKB_GSO_TCP_ECN gso_type bit +to be set. + +Fixes: a57e5de476be ("gve: DQO: Add TX path") +Signed-off-by: Joshua Washington +Reviewed-by: Praveen Kaligineedi +Reviewed-by: Harshitha Ramamurthy +Reviewed-by: Willem de Bruijn +Suggested-by: Eric Dumazet +Acked-by: Andrei Vagin + +v2 - Remove unnecessary comments, remove line break between fixes tag +and signoffs. + +v3 - Add back unrelated empty line removal. + +Link: https://lore.kernel.org/r/20240610225729.2985343-1-joshwash@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/google/gve/gve_tx_dqo.c | 20 +++++--------------- + 1 file changed, 5 insertions(+), 15 deletions(-) + +diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c +index e84e944d751d2..5147fb37929e0 100644 +--- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c ++++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c +@@ -370,28 +370,18 @@ static int gve_prep_tso(struct sk_buff *skb) + if (unlikely(skb_shinfo(skb)->gso_size < GVE_TX_MIN_TSO_MSS_DQO)) + return -1; + ++ if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) ++ return -EINVAL; ++ + /* Needed because we will modify header. */ + err = skb_cow_head(skb, 0); + if (err < 0) + return err; + + tcp = tcp_hdr(skb); +- +- /* Remove payload length from checksum. */ + paylen = skb->len - skb_transport_offset(skb); +- +- switch (skb_shinfo(skb)->gso_type) { +- case SKB_GSO_TCPV4: +- case SKB_GSO_TCPV6: +- csum_replace_by_diff(&tcp->check, +- (__force __wsum)htonl(paylen)); +- +- /* Compute length of segmentation header. */ +- header_len = skb_tcp_all_headers(skb); +- break; +- default: +- return -EINVAL; +- } ++ csum_replace_by_diff(&tcp->check, (__force __wsum)htonl(paylen)); ++ header_len = skb_tcp_all_headers(skb); + + if (unlikely(header_len > GVE_TX_MAX_HDR_SIZE_DQO)) + return -EINVAL; +-- +2.43.0 + diff --git a/queue-6.1/hid-core-remove-unnecessary-warn_on-in-implement.patch b/queue-6.1/hid-core-remove-unnecessary-warn_on-in-implement.patch new file mode 100644 index 00000000000..210d88953ca --- /dev/null +++ b/queue-6.1/hid-core-remove-unnecessary-warn_on-in-implement.patch @@ -0,0 +1,67 @@ +From 03c6b01636a2864dbba970c6ac8562a5aed4f23d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 May 2024 07:19:14 -0700 +Subject: HID: core: remove unnecessary WARN_ON() in implement() + +From: Nikita Zhandarovich + +[ Upstream commit 4aa2dcfbad538adf7becd0034a3754e1bd01b2b5 ] + +Syzkaller hit a warning [1] in a call to implement() when trying +to write a value into a field of smaller size in an output report. + +Since implement() already has a warn message printed out with the +help of hid_warn() and value in question gets trimmed with: + ... + value &= m; + ... +WARN_ON may be considered superfluous. Remove it to suppress future +syzkaller triggers. + +[1] +WARNING: CPU: 0 PID: 5084 at drivers/hid/hid-core.c:1451 implement drivers/hid/hid-core.c:1451 [inline] +WARNING: CPU: 0 PID: 5084 at drivers/hid/hid-core.c:1451 hid_output_report+0x548/0x760 drivers/hid/hid-core.c:1863 +Modules linked in: +CPU: 0 PID: 5084 Comm: syz-executor424 Not tainted 6.9.0-rc7-syzkaller-00183-gcf87f46fd34d #0 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/02/2024 +RIP: 0010:implement drivers/hid/hid-core.c:1451 [inline] +RIP: 0010:hid_output_report+0x548/0x760 drivers/hid/hid-core.c:1863 +... +Call Trace: + + __usbhid_submit_report drivers/hid/usbhid/hid-core.c:591 [inline] + usbhid_submit_report+0x43d/0x9e0 drivers/hid/usbhid/hid-core.c:636 + hiddev_ioctl+0x138b/0x1f00 drivers/hid/usbhid/hiddev.c:726 + vfs_ioctl fs/ioctl.c:51 [inline] + __do_sys_ioctl fs/ioctl.c:904 [inline] + __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:890 + do_syscall_x64 arch/x86/entry/common.c:52 [inline] + do_syscall_64+0xf5/0x240 arch/x86/entry/common.c:83 + entry_SYSCALL_64_after_hwframe+0x77/0x7f +... + +Fixes: 95d1c8951e5b ("HID: simplify implement() a bit") +Reported-by: +Suggested-by: Alan Stern +Signed-off-by: Nikita Zhandarovich +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-core.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index cdad3a0662876..e2e52aa0eeba9 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1451,7 +1451,6 @@ static void implement(const struct hid_device *hid, u8 *report, + hid_warn(hid, + "%s() called with too large value %d (n: %d)! (%s)\n", + __func__, value, n, current->comm); +- WARN_ON(1); + value &= m; + } + } +-- +2.43.0 + diff --git a/queue-6.1/hid-logitech-dj-fix-memory-leak-in-logi_dj_recv_swit.patch b/queue-6.1/hid-logitech-dj-fix-memory-leak-in-logi_dj_recv_swit.patch new file mode 100644 index 00000000000..cb1ea65f47b --- /dev/null +++ b/queue-6.1/hid-logitech-dj-fix-memory-leak-in-logi_dj_recv_swit.patch @@ -0,0 +1,41 @@ +From a6519a5637f6e36fdcf0df44458a0785197ea84a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 May 2024 15:05:39 +0200 +Subject: HID: logitech-dj: Fix memory leak in logi_dj_recv_switch_to_dj_mode() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: José Expósito + +[ Upstream commit ce3af2ee95170b7d9e15fff6e500d67deab1e7b3 ] + +Fix a memory leak on logi_dj_recv_send_report() error path. + +Fixes: 6f20d3261265 ("HID: logitech-dj: Fix error handling in logi_dj_recv_switch_to_dj_mode()") +Signed-off-by: José Expósito +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-logitech-dj.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c +index 57697605b2e24..dc7b0fe83478e 100644 +--- a/drivers/hid/hid-logitech-dj.c ++++ b/drivers/hid/hid-logitech-dj.c +@@ -1284,8 +1284,10 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, + */ + msleep(50); + +- if (retval) ++ if (retval) { ++ kfree(dj_report); + return retval; ++ } + } + + /* +-- +2.43.0 + diff --git a/queue-6.1/iommu-amd-fix-sysfs-leak-in-iommu-init.patch b/queue-6.1/iommu-amd-fix-sysfs-leak-in-iommu-init.patch new file mode 100644 index 00000000000..8835810ae2a --- /dev/null +++ b/queue-6.1/iommu-amd-fix-sysfs-leak-in-iommu-init.patch @@ -0,0 +1,47 @@ +From b49915176559697088a0eb68874c1b15af6123d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 May 2024 08:42:20 +0800 +Subject: iommu/amd: Fix sysfs leak in iommu init + +From: Kun(llfl) + +[ Upstream commit a295ec52c8624883885396fde7b4df1a179627c3 ] + +During the iommu initialization, iommu_init_pci() adds sysfs nodes. +However, these nodes aren't remove in free_iommu_resources() subsequently. + +Fixes: 39ab9555c241 ("iommu: Add sysfs bindings for struct iommu_device") +Signed-off-by: Kun(llfl) +Reviewed-by: Suravee Suthikulpanit +Link: https://lore.kernel.org/r/c8e0d11c6ab1ee48299c288009cf9c5dae07b42d.1715215003.git.llfl@linux.alibaba.com +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + drivers/iommu/amd/init.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c +index cc94ac6662339..c9598c506ff94 100644 +--- a/drivers/iommu/amd/init.c ++++ b/drivers/iommu/amd/init.c +@@ -1655,8 +1655,17 @@ static void __init free_pci_segments(void) + } + } + ++static void __init free_sysfs(struct amd_iommu *iommu) ++{ ++ if (iommu->iommu.dev) { ++ iommu_device_unregister(&iommu->iommu); ++ iommu_device_sysfs_remove(&iommu->iommu); ++ } ++} ++ + static void __init free_iommu_one(struct amd_iommu *iommu) + { ++ free_sysfs(iommu); + free_cwwb_sem(iommu); + free_command_buffer(iommu); + free_event_buffer(iommu); +-- +2.43.0 + diff --git a/queue-6.1/ionic-fix-use-after-netif_napi_del.patch b/queue-6.1/ionic-fix-use-after-netif_napi_del.patch new file mode 100644 index 00000000000..1f1707b9d37 --- /dev/null +++ b/queue-6.1/ionic-fix-use-after-netif_napi_del.patch @@ -0,0 +1,97 @@ +From 258c19e3342f514e0ca2ae130591a36d7a045ab7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 12 Jun 2024 06:04:46 +0000 +Subject: ionic: fix use after netif_napi_del() + +From: Taehee Yoo + +[ Upstream commit 79f18a41dd056115d685f3b0a419c7cd40055e13 ] + +When queues are started, netif_napi_add() and napi_enable() are called. +If there are 4 queues and only 3 queues are used for the current +configuration, only 3 queues' napi should be registered and enabled. +The ionic_qcq_enable() checks whether the .poll pointer is not NULL for +enabling only the using queue' napi. Unused queues' napi will not be +registered by netif_napi_add(), so the .poll pointer indicates NULL. +But it couldn't distinguish whether the napi was unregistered or not +because netif_napi_del() doesn't reset the .poll pointer to NULL. +So, ionic_qcq_enable() calls napi_enable() for the queue, which was +unregistered by netif_napi_del(). + +Reproducer: + ethtool -L rx 1 tx 1 combined 0 + ethtool -L rx 0 tx 0 combined 1 + ethtool -L rx 0 tx 0 combined 4 + +Splat looks like: +kernel BUG at net/core/dev.c:6666! +Oops: invalid opcode: 0000 [#1] PREEMPT SMP NOPTI +CPU: 3 PID: 1057 Comm: kworker/3:3 Not tainted 6.10.0-rc2+ #16 +Workqueue: events ionic_lif_deferred_work [ionic] +RIP: 0010:napi_enable+0x3b/0x40 +Code: 48 89 c2 48 83 e2 f6 80 b9 61 09 00 00 00 74 0d 48 83 bf 60 01 00 00 00 74 03 80 ce 01 f0 4f +RSP: 0018:ffffb6ed83227d48 EFLAGS: 00010246 +RAX: 0000000000000000 RBX: ffff97560cda0828 RCX: 0000000000000029 +RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffff97560cda0a28 +RBP: ffffb6ed83227d50 R08: 0000000000000400 R09: 0000000000000001 +R10: 0000000000000001 R11: 0000000000000001 R12: 0000000000000000 +R13: ffff97560ce3c1a0 R14: 0000000000000000 R15: ffff975613ba0a20 +FS: 0000000000000000(0000) GS:ffff975d5f780000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007f8f734ee200 CR3: 0000000103e50000 CR4: 00000000007506f0 +PKRU: 55555554 +Call Trace: + + ? die+0x33/0x90 + ? do_trap+0xd9/0x100 + ? napi_enable+0x3b/0x40 + ? do_error_trap+0x83/0xb0 + ? napi_enable+0x3b/0x40 + ? napi_enable+0x3b/0x40 + ? exc_invalid_op+0x4e/0x70 + ? napi_enable+0x3b/0x40 + ? asm_exc_invalid_op+0x16/0x20 + ? napi_enable+0x3b/0x40 + ionic_qcq_enable+0xb7/0x180 [ionic 59bdfc8a035436e1c4224ff7d10789e3f14643f8] + ionic_start_queues+0xc4/0x290 [ionic 59bdfc8a035436e1c4224ff7d10789e3f14643f8] + ionic_link_status_check+0x11c/0x170 [ionic 59bdfc8a035436e1c4224ff7d10789e3f14643f8] + ionic_lif_deferred_work+0x129/0x280 [ionic 59bdfc8a035436e1c4224ff7d10789e3f14643f8] + process_one_work+0x145/0x360 + worker_thread+0x2bb/0x3d0 + ? __pfx_worker_thread+0x10/0x10 + kthread+0xcc/0x100 + ? __pfx_kthread+0x10/0x10 + ret_from_fork+0x2d/0x50 + ? __pfx_kthread+0x10/0x10 + ret_from_fork_asm+0x1a/0x30 + +Fixes: 0f3154e6bcb3 ("ionic: Add Tx and Rx handling") +Signed-off-by: Taehee Yoo +Reviewed-by: Brett Creeley +Reviewed-by: Shannon Nelson +Link: https://lore.kernel.org/r/20240612060446.1754392-1-ap420073@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/pensando/ionic/ionic_lif.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c +index d33cf8ee7c336..d34aea85f8a69 100644 +--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c ++++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c +@@ -292,10 +292,8 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq) + if (ret) + return ret; + +- if (qcq->napi.poll) +- napi_enable(&qcq->napi); +- + if (qcq->flags & IONIC_QCQ_F_INTR) { ++ napi_enable(&qcq->napi); + irq_set_affinity_hint(qcq->intr.vector, + &qcq->intr.affinity_mask); + ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, +-- +2.43.0 + diff --git a/queue-6.1/liquidio-adjust-a-null-pointer-handling-path-in-lio_.patch b/queue-6.1/liquidio-adjust-a-null-pointer-handling-path-in-lio_.patch new file mode 100644 index 00000000000..c7a239f655d --- /dev/null +++ b/queue-6.1/liquidio-adjust-a-null-pointer-handling-path-in-lio_.patch @@ -0,0 +1,69 @@ +From e1e7a9367469b8a81d501fbf5e1673886ccbca3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Jun 2024 13:11:35 +0300 +Subject: liquidio: Adjust a NULL pointer handling path in + lio_vf_rep_copy_packet + +From: Aleksandr Mishin + +[ Upstream commit c44711b78608c98a3e6b49ce91678cd0917d5349 ] + +In lio_vf_rep_copy_packet() pg_info->page is compared to a NULL value, +but then it is unconditionally passed to skb_add_rx_frag() which looks +strange and could lead to null pointer dereference. + +lio_vf_rep_copy_packet() call trace looks like: + octeon_droq_process_packets + octeon_droq_fast_process_packets + octeon_droq_dispatch_pkt + octeon_create_recv_info + ...search in the dispatch_list... + ->disp_fn(rdisp->rinfo, ...) + lio_vf_rep_pkt_recv(struct octeon_recv_info *recv_info, ...) +In this path there is no code which sets pg_info->page to NULL. +So this check looks unneeded and doesn't solve potential problem. +But I guess the author had reason to add a check and I have no such card +and can't do real test. +In addition, the code in the function liquidio_push_packet() in +liquidio/lio_core.c does exactly the same. + +Based on this, I consider the most acceptable compromise solution to +adjust this issue by moving skb_add_rx_frag() into conditional scope. + +Found by Linux Verification Center (linuxtesting.org) with SVACE. + +Fixes: 1f233f327913 ("liquidio: switchdev support for LiquidIO NIC") +Signed-off-by: Aleksandr Mishin +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c +index 600de587d7a98..e70b9ccca380e 100644 +--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c ++++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c +@@ -272,13 +272,12 @@ lio_vf_rep_copy_packet(struct octeon_device *oct, + pg_info->page_offset; + memcpy(skb->data, va, MIN_SKB_SIZE); + skb_put(skb, MIN_SKB_SIZE); ++ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, ++ pg_info->page, ++ pg_info->page_offset + MIN_SKB_SIZE, ++ len - MIN_SKB_SIZE, ++ LIO_RXBUFFER_SZ); + } +- +- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, +- pg_info->page, +- pg_info->page_offset + MIN_SKB_SIZE, +- len - MIN_SKB_SIZE, +- LIO_RXBUFFER_SZ); + } else { + struct octeon_skb_page_info *pg_info = + ((struct octeon_skb_page_info *)(skb->cb)); +-- +2.43.0 + diff --git a/queue-6.1/net-bridge-mst-fix-suspicious-rcu-usage-in-br_mst_se.patch b/queue-6.1/net-bridge-mst-fix-suspicious-rcu-usage-in-br_mst_se.patch new file mode 100644 index 00000000000..d162069b3d3 --- /dev/null +++ b/queue-6.1/net-bridge-mst-fix-suspicious-rcu-usage-in-br_mst_se.patch @@ -0,0 +1,40 @@ +From 0e590181b2aa50c2e5523452f49b07b3bdeb4d84 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Jun 2024 13:36:54 +0300 +Subject: net: bridge: mst: fix suspicious rcu usage in br_mst_set_state + +From: Nikolay Aleksandrov + +[ Upstream commit 546ceb1dfdac866648ec959cbc71d9525bd73462 ] + +I converted br_mst_set_state to RCU to avoid a vlan use-after-free +but forgot to change the vlan group dereference helper. Switch to vlan +group RCU deref helper to fix the suspicious rcu usage warning. + +Fixes: 3a7c1661ae13 ("net: bridge: mst: fix vlan use-after-free") +Reported-by: syzbot+9bbe2de1bc9d470eb5fe@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9bbe2de1bc9d470eb5fe +Signed-off-by: Nikolay Aleksandrov +Link: https://lore.kernel.org/r/20240609103654.914987-3-razor@blackwall.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mst.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c +index 1de72816b0fb2..1820f09ff59ce 100644 +--- a/net/bridge/br_mst.c ++++ b/net/bridge/br_mst.c +@@ -102,7 +102,7 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state, + int err = 0; + + rcu_read_lock(); +- vg = nbp_vlan_group(p); ++ vg = nbp_vlan_group_rcu(p); + if (!vg) + goto out; + +-- +2.43.0 + diff --git a/queue-6.1/net-bridge-mst-pass-vlan-group-directly-to-br_mst_vl.patch b/queue-6.1/net-bridge-mst-pass-vlan-group-directly-to-br_mst_vl.patch new file mode 100644 index 00000000000..aee5c08da01 --- /dev/null +++ b/queue-6.1/net-bridge-mst-pass-vlan-group-directly-to-br_mst_vl.patch @@ -0,0 +1,72 @@ +From 2766cf15f14460c9c78328a5f1b8e9b9afb01daa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Jun 2024 13:36:53 +0300 +Subject: net: bridge: mst: pass vlan group directly to br_mst_vlan_set_state + +From: Nikolay Aleksandrov + +[ Upstream commit 36c92936e868601fa1f43da6758cf55805043509 ] + +Pass the already obtained vlan group pointer to br_mst_vlan_set_state() +instead of dereferencing it again. Each caller has already correctly +dereferenced it for their context. This change is required for the +following suspicious RCU dereference fix. No functional changes +intended. + +Fixes: 3a7c1661ae13 ("net: bridge: mst: fix vlan use-after-free") +Reported-by: syzbot+9bbe2de1bc9d470eb5fe@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9bbe2de1bc9d470eb5fe +Signed-off-by: Nikolay Aleksandrov +Link: https://lore.kernel.org/r/20240609103654.914987-2-razor@blackwall.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mst.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c +index 3c66141d34d62..1de72816b0fb2 100644 +--- a/net/bridge/br_mst.c ++++ b/net/bridge/br_mst.c +@@ -73,11 +73,10 @@ int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state) + } + EXPORT_SYMBOL_GPL(br_mst_get_state); + +-static void br_mst_vlan_set_state(struct net_bridge_port *p, struct net_bridge_vlan *v, ++static void br_mst_vlan_set_state(struct net_bridge_vlan_group *vg, ++ struct net_bridge_vlan *v, + u8 state) + { +- struct net_bridge_vlan_group *vg = nbp_vlan_group(p); +- + if (br_vlan_get_state(v) == state) + return; + +@@ -121,7 +120,7 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state, + if (v->brvlan->msti != msti) + continue; + +- br_mst_vlan_set_state(p, v, state); ++ br_mst_vlan_set_state(vg, v, state); + } + + out: +@@ -140,13 +139,13 @@ static void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti) + * it. + */ + if (v != pv && v->brvlan->msti == msti) { +- br_mst_vlan_set_state(pv->port, pv, v->state); ++ br_mst_vlan_set_state(vg, pv, v->state); + return; + } + } + + /* Otherwise, start out in a new MSTI with all ports disabled. */ +- return br_mst_vlan_set_state(pv->port, pv, BR_STATE_DISABLED); ++ return br_mst_vlan_set_state(vg, pv, BR_STATE_DISABLED); + } + + int br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti) +-- +2.43.0 + diff --git a/queue-6.1/net-hns3-add-cond_resched-to-hns3-ring-buffer-init-p.patch b/queue-6.1/net-hns3-add-cond_resched-to-hns3-ring-buffer-init-p.patch new file mode 100644 index 00000000000..20fc1fb23e1 --- /dev/null +++ b/queue-6.1/net-hns3-add-cond_resched-to-hns3-ring-buffer-init-p.patch @@ -0,0 +1,64 @@ +From 6d0b7b0fc40778fc5cc5bf466f0547a8fe558078 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Jun 2024 15:20:58 +0800 +Subject: net: hns3: add cond_resched() to hns3 ring buffer init process + +From: Jie Wang + +[ Upstream commit 968fde83841a8c23558dfbd0a0c69d636db52b55 ] + +Currently hns3 ring buffer init process would hold cpu too long with big +Tx/Rx ring depth. This could cause soft lockup. + +So this patch adds cond_resched() to the process. Then cpu can break to +run other tasks instead of busy looping. + +Fixes: a723fb8efe29 ("net: hns3: refine for set ring parameters") +Signed-off-by: Jie Wang +Signed-off-by: Jijie Shao +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 4 ++++ + drivers/net/ethernet/hisilicon/hns3/hns3_enet.h | 2 ++ + 2 files changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +index 78d6752fe0519..4ce43c3a00a37 100644 +--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c ++++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +@@ -3538,6 +3538,9 @@ static int hns3_alloc_ring_buffers(struct hns3_enet_ring *ring) + ret = hns3_alloc_and_attach_buffer(ring, i); + if (ret) + goto out_buffer_fail; ++ ++ if (!(i % HNS3_RESCHED_BD_NUM)) ++ cond_resched(); + } + + return 0; +@@ -5111,6 +5114,7 @@ int hns3_init_all_ring(struct hns3_nic_priv *priv) + } + + u64_stats_init(&priv->ring[i].syncp); ++ cond_resched(); + } + + return 0; +diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +index 294a14b4fdefb..1aac93f9aaa15 100644 +--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h ++++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +@@ -214,6 +214,8 @@ enum hns3_nic_state { + #define HNS3_CQ_MODE_EQE 1U + #define HNS3_CQ_MODE_CQE 0U + ++#define HNS3_RESCHED_BD_NUM 1024 ++ + enum hns3_pkt_l2t_type { + HNS3_L2_TYPE_UNICAST, + HNS3_L2_TYPE_MULTICAST, +-- +2.43.0 + diff --git a/queue-6.1/net-hns3-fix-kernel-crash-problem-in-concurrent-scen.patch b/queue-6.1/net-hns3-fix-kernel-crash-problem-in-concurrent-scen.patch new file mode 100644 index 00000000000..5540c7d5fa1 --- /dev/null +++ b/queue-6.1/net-hns3-fix-kernel-crash-problem-in-concurrent-scen.patch @@ -0,0 +1,84 @@ +From bc374fae8d27614ba806f4a57fe418af03b061f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Jun 2024 15:20:57 +0800 +Subject: net: hns3: fix kernel crash problem in concurrent scenario + +From: Yonglong Liu + +[ Upstream commit 12cda920212a49fa22d9e8b9492ac4ea013310a4 ] + +When link status change, the nic driver need to notify the roce +driver to handle this event, but at this time, the roce driver +may uninit, then cause kernel crash. + +To fix the problem, when link status change, need to check +whether the roce registered, and when uninit, need to wait link +update finish. + +Fixes: 45e92b7e4e27 ("net: hns3: add calling roce callback function when link status change") +Signed-off-by: Yonglong Liu +Signed-off-by: Jijie Shao +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + .../hisilicon/hns3/hns3pf/hclge_main.c | 21 ++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +index a2655adc764cd..01e24b69e9203 100644 +--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c ++++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +@@ -3129,9 +3129,7 @@ static void hclge_push_link_status(struct hclge_dev *hdev) + + static void hclge_update_link_status(struct hclge_dev *hdev) + { +- struct hnae3_handle *rhandle = &hdev->vport[0].roce; + struct hnae3_handle *handle = &hdev->vport[0].nic; +- struct hnae3_client *rclient = hdev->roce_client; + struct hnae3_client *client = hdev->nic_client; + int state; + int ret; +@@ -3155,8 +3153,15 @@ static void hclge_update_link_status(struct hclge_dev *hdev) + + client->ops->link_status_change(handle, state); + hclge_config_mac_tnl_int(hdev, state); +- if (rclient && rclient->ops->link_status_change) +- rclient->ops->link_status_change(rhandle, state); ++ ++ if (test_bit(HCLGE_STATE_ROCE_REGISTERED, &hdev->state)) { ++ struct hnae3_handle *rhandle = &hdev->vport[0].roce; ++ struct hnae3_client *rclient = hdev->roce_client; ++ ++ if (rclient && rclient->ops->link_status_change) ++ rclient->ops->link_status_change(rhandle, ++ state); ++ } + + hclge_push_link_status(hdev); + } +@@ -11339,6 +11344,12 @@ static int hclge_init_client_instance(struct hnae3_client *client, + return ret; + } + ++static bool hclge_uninit_need_wait(struct hclge_dev *hdev) ++{ ++ return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) || ++ test_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state); ++} ++ + static void hclge_uninit_client_instance(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev) + { +@@ -11347,7 +11358,7 @@ static void hclge_uninit_client_instance(struct hnae3_client *client, + + if (hdev->roce_client) { + clear_bit(HCLGE_STATE_ROCE_REGISTERED, &hdev->state); +- while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) ++ while (hclge_uninit_need_wait(hdev)) + msleep(HCLGE_WAIT_RESET_DONE); + + hdev->roce_client->ops->uninit_instance(&vport->roce, 0); +-- +2.43.0 + diff --git a/queue-6.1/net-ipv6-fix-the-rt-cache-flush-via-sysctl-using-a-p.patch b/queue-6.1/net-ipv6-fix-the-rt-cache-flush-via-sysctl-using-a-p.patch new file mode 100644 index 00000000000..064a7097566 --- /dev/null +++ b/queue-6.1/net-ipv6-fix-the-rt-cache-flush-via-sysctl-using-a-p.patch @@ -0,0 +1,53 @@ +From cdc964e3344b88619b8925ccec0c6ac4392af4d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 7 Jun 2024 13:28:28 +0200 +Subject: net/ipv6: Fix the RT cache flush via sysctl using a previous delay + +From: Petr Pavlu + +[ Upstream commit 14a20e5b4ad998793c5f43b0330d9e1388446cf3 ] + +The net.ipv6.route.flush system parameter takes a value which specifies +a delay used during the flush operation for aging exception routes. The +written value is however not used in the currently requested flush and +instead utilized only in the next one. + +A problem is that ipv6_sysctl_rtcache_flush() first reads the old value +of net->ipv6.sysctl.flush_delay into a local delay variable and then +calls proc_dointvec() which actually updates the sysctl based on the +provided input. + +Fix the problem by switching the order of the two operations. + +Fixes: 4990509f19e8 ("[NETNS][IPV6]: Make sysctls route per namespace.") +Signed-off-by: Petr Pavlu +Reviewed-by: David Ahern +Link: https://lore.kernel.org/r/20240607112828.30285-1-petr.pavlu@suse.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 627431722f9d6..d305051e8ab5f 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6335,12 +6335,12 @@ static int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, + if (!write) + return -EINVAL; + +- net = (struct net *)ctl->extra1; +- delay = net->ipv6.sysctl.flush_delay; + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + if (ret) + return ret; + ++ net = (struct net *)ctl->extra1; ++ delay = net->ipv6.sysctl.flush_delay; + fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); + return 0; + } +-- +2.43.0 + diff --git a/queue-6.1/net-mlx5e-fix-features-validation-check-for-tunneled.patch b/queue-6.1/net-mlx5e-fix-features-validation-check-for-tunneled.patch new file mode 100644 index 00000000000..0034c5e0f47 --- /dev/null +++ b/queue-6.1/net-mlx5e-fix-features-validation-check-for-tunneled.patch @@ -0,0 +1,53 @@ +From dc27cb0512dbd53bd3b0817f11aea9b90d85ad6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Jun 2024 23:32:49 +0300 +Subject: net/mlx5e: Fix features validation check for tunneled UDP (non-VXLAN) + packets + +From: Gal Pressman + +[ Upstream commit 791b4089e326271424b78f2fae778b20e53d071b ] + +Move the vxlan_features_check() call to after we verified the packet is +a tunneled VXLAN packet. + +Without this, tunneled UDP non-VXLAN packets (for ex. GENENVE) might +wrongly not get offloaded. +In some cases, it worked by chance as GENEVE header is the same size as +VXLAN, but it is obviously incorrect. + +Fixes: e3cfc7e6b7bd ("net/mlx5e: TX, Add geneve tunnel stateless offload support") +Signed-off-by: Gal Pressman +Reviewed-by: Dragos Tatulea +Signed-off-by: Tariq Toukan +Reviewed-by: Wojciech Drewek +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index e2f134e1d9fcf..4c0eac83546de 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -4587,7 +4587,7 @@ static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv, + + /* Verify if UDP port is being offloaded by HW */ + if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, port)) +- return features; ++ return vxlan_features_check(skb, features); + + #if IS_ENABLED(CONFIG_GENEVE) + /* Support Geneve offload for default UDP port */ +@@ -4613,7 +4613,6 @@ netdev_features_t mlx5e_features_check(struct sk_buff *skb, + struct mlx5e_priv *priv = netdev_priv(netdev); + + features = vlan_features_check(skb, features); +- features = vxlan_features_check(skb, features); + + /* Validate if the tunneled packet is being offloaded by HW */ + if (skb->encapsulation && +-- +2.43.0 + diff --git a/queue-6.1/net-pse-pd-use-eopnotsupp-error-code-instead-of-enot.patch b/queue-6.1/net-pse-pd-use-eopnotsupp-error-code-instead-of-enot.patch new file mode 100644 index 00000000000..4ac62a45e7e --- /dev/null +++ b/queue-6.1/net-pse-pd-use-eopnotsupp-error-code-instead-of-enot.patch @@ -0,0 +1,47 @@ +From 1f1840123cb99fabd1d9ce0c603060c6c5bcf2fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 10 Jun 2024 10:34:26 +0200 +Subject: net: pse-pd: Use EOPNOTSUPP error code instead of ENOTSUPP + +From: Kory Maincent + +[ Upstream commit 144ba8580bcb82b2686c3d1a043299d844b9a682 ] + +ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP as reported by +checkpatch script. + +Fixes: 18ff0bcda6d1 ("ethtool: add interface to interact with Ethernet Power Equipment") +Reviewed-by: Andrew Lunn +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Link: https://lore.kernel.org/r/20240610083426.740660-1-kory.maincent@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/linux/pse-pd/pse.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h +index fb724c65c77bc..5ce0cd76956e0 100644 +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -114,14 +114,14 @@ static inline int pse_ethtool_get_status(struct pse_control *psec, + struct netlink_ext_ack *extack, + struct pse_control_status *status) + { +- return -ENOTSUPP; ++ return -EOPNOTSUPP; + } + + static inline int pse_ethtool_set_config(struct pse_control *psec, + struct netlink_ext_ack *extack, + const struct pse_control_config *config) + { +- return -ENOTSUPP; ++ return -EOPNOTSUPP; + } + + #endif +-- +2.43.0 + diff --git a/queue-6.1/net-sfp-always-call-sfp_sm_mod_remove-on-remove.patch b/queue-6.1/net-sfp-always-call-sfp_sm_mod_remove-on-remove.patch new file mode 100644 index 00000000000..7c2bc4fc8dc --- /dev/null +++ b/queue-6.1/net-sfp-always-call-sfp_sm_mod_remove-on-remove.patch @@ -0,0 +1,45 @@ +From 692aa3117997dadc26c17c13e85f468eba35d95b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Jun 2024 10:42:51 +0200 +Subject: net: sfp: Always call `sfp_sm_mod_remove()` on remove +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Csókás, Bence + +[ Upstream commit e96b2933152fd87b6a41765b2f58b158fde855b6 ] + +If the module is in SFP_MOD_ERROR, `sfp_sm_mod_remove()` will +not be run. As a consequence, `sfp_hwmon_remove()` is not getting +run either, leaving a stale `hwmon` device behind. `sfp_sm_mod_remove()` +itself checks `sfp->sm_mod_state` anyways, so this check was not +really needed in the first place. + +Fixes: d2e816c0293f ("net: sfp: handle module remove outside state machine") +Signed-off-by: "Csókás, Bence" +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240605084251.63502-1-csokas.bence@prolan.hu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/sfp.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 9b1403291d921..06dce78d7b0c9 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -2150,8 +2150,7 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) + + /* Handle remove event globally, it resets this state machine */ + if (event == SFP_E_REMOVE) { +- if (sfp->sm_mod_state > SFP_MOD_PROBE) +- sfp_sm_mod_remove(sfp); ++ sfp_sm_mod_remove(sfp); + sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); + return; + } +-- +2.43.0 + diff --git a/queue-6.1/net-stmmac-replace-priv-speed-with-the-porttransmitr.patch b/queue-6.1/net-stmmac-replace-priv-speed-with-the-porttransmitr.patch new file mode 100644 index 00000000000..e270c601e25 --- /dev/null +++ b/queue-6.1/net-stmmac-replace-priv-speed-with-the-porttransmitr.patch @@ -0,0 +1,101 @@ +From 7a65ddcadcdf6d42013551fa67a7729a400487df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 8 Jun 2024 22:35:24 +0800 +Subject: net: stmmac: replace priv->speed with the portTransmitRate from the + tc-cbs parameters + +From: Xiaolei Wang + +[ Upstream commit be27b896529787e23a35ae4befb6337ce73fcca0 ] + +The current cbs parameter depends on speed after uplinking, +which is not needed and will report a configuration error +if the port is not initially connected. The UAPI exposed by +tc-cbs requires userspace to recalculate the send slope anyway, +because the formula depends on port_transmit_rate (see man tc-cbs), +which is not an invariant from tc's perspective. Therefore, we +use offload->sendslope and offload->idleslope to derive the +original port_transmit_rate from the CBS formula. + +Fixes: 1f705bc61aee ("net: stmmac: Add support for CBS QDISC") +Signed-off-by: Xiaolei Wang +Reviewed-by: Wojciech Drewek +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240608143524.2065736-1-xiaolei.wang@windriver.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 25 ++++++++----------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +index 390c900832cd2..074ff289eaf25 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +@@ -343,10 +343,11 @@ static int tc_setup_cbs(struct stmmac_priv *priv, + struct tc_cbs_qopt_offload *qopt) + { + u32 tx_queues_count = priv->plat->tx_queues_to_use; ++ s64 port_transmit_rate_kbps; + u32 queue = qopt->queue; +- u32 ptr, speed_div; + u32 mode_to_use; + u64 value; ++ u32 ptr; + int ret; + + /* Queue 0 is not AVB capable */ +@@ -355,30 +356,26 @@ static int tc_setup_cbs(struct stmmac_priv *priv, + if (!priv->dma_cap.av) + return -EOPNOTSUPP; + ++ port_transmit_rate_kbps = qopt->idleslope - qopt->sendslope; ++ + /* Port Transmit Rate and Speed Divider */ +- switch (priv->speed) { ++ switch (div_s64(port_transmit_rate_kbps, 1000)) { + case SPEED_10000: +- ptr = 32; +- speed_div = 10000000; +- break; + case SPEED_5000: + ptr = 32; +- speed_div = 5000000; + break; + case SPEED_2500: +- ptr = 8; +- speed_div = 2500000; +- break; + case SPEED_1000: + ptr = 8; +- speed_div = 1000000; + break; + case SPEED_100: + ptr = 4; +- speed_div = 100000; + break; + default: +- return -EOPNOTSUPP; ++ netdev_err(priv->dev, ++ "Invalid portTransmitRate %lld (idleSlope - sendSlope)\n", ++ port_transmit_rate_kbps); ++ return -EINVAL; + } + + mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use; +@@ -398,10 +395,10 @@ static int tc_setup_cbs(struct stmmac_priv *priv, + } + + /* Final adjustments for HW */ +- value = div_s64(qopt->idleslope * 1024ll * ptr, speed_div); ++ value = div_s64(qopt->idleslope * 1024ll * ptr, port_transmit_rate_kbps); + priv->plat->tx_queues_cfg[queue].idle_slope = value & GENMASK(31, 0); + +- value = div_s64(-qopt->sendslope * 1024ll * ptr, speed_div); ++ value = div_s64(-qopt->sendslope * 1024ll * ptr, port_transmit_rate_kbps); + priv->plat->tx_queues_cfg[queue].send_slope = value & GENMASK(31, 0); + + value = qopt->hicredit * 1024ll * 8; +-- +2.43.0 + diff --git a/queue-6.1/netfilter-ipset-fix-race-between-namespace-cleanup-a.patch b/queue-6.1/netfilter-ipset-fix-race-between-namespace-cleanup-a.patch new file mode 100644 index 00000000000..e2d57ecf8bd --- /dev/null +++ b/queue-6.1/netfilter-ipset-fix-race-between-namespace-cleanup-a.patch @@ -0,0 +1,289 @@ +From 174e4426067de245b35d03a3b6aa5ccbb6f180da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Jun 2024 15:58:03 +0200 +Subject: netfilter: ipset: Fix race between namespace cleanup and gc in the + list:set type + +From: Jozsef Kadlecsik + +[ Upstream commit 4e7aaa6b82d63e8ddcbfb56b4fd3d014ca586f10 ] + +Lion Ackermann reported that there is a race condition between namespace cleanup +in ipset and the garbage collection of the list:set type. The namespace +cleanup can destroy the list:set type of sets while the gc of the set type is +waiting to run in rcu cleanup. The latter uses data from the destroyed set which +thus leads use after free. The patch contains the following parts: + +- When destroying all sets, first remove the garbage collectors, then wait + if needed and then destroy the sets. +- Fix the badly ordered "wait then remove gc" for the destroy a single set + case. +- Fix the missing rcu locking in the list:set type in the userspace test + case. +- Use proper RCU list handlings in the list:set type. + +The patch depends on c1193d9bbbd3 (netfilter: ipset: Add list flush to cancel_gc). + +Fixes: 97f7cf1cd80e (netfilter: ipset: fix performance regression in swap operation) +Reported-by: Lion Ackermann +Tested-by: Lion Ackermann +Signed-off-by: Jozsef Kadlecsik +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/ipset/ip_set_core.c | 81 +++++++++++++++------------ + net/netfilter/ipset/ip_set_list_set.c | 30 +++++----- + 2 files changed, 60 insertions(+), 51 deletions(-) + +diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c +index f645da82d826e..649b8a5901e33 100644 +--- a/net/netfilter/ipset/ip_set_core.c ++++ b/net/netfilter/ipset/ip_set_core.c +@@ -1174,23 +1174,50 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { + .len = IPSET_MAXNAMELEN - 1 }, + }; + ++/* In order to return quickly when destroying a single set, it is split ++ * into two stages: ++ * - Cancel garbage collector ++ * - Destroy the set itself via call_rcu() ++ */ ++ + static void +-ip_set_destroy_set(struct ip_set *set) ++ip_set_destroy_set_rcu(struct rcu_head *head) + { +- pr_debug("set: %s\n", set->name); ++ struct ip_set *set = container_of(head, struct ip_set, rcu); + +- /* Must call it without holding any lock */ + set->variant->destroy(set); + module_put(set->type->me); + kfree(set); + } + + static void +-ip_set_destroy_set_rcu(struct rcu_head *head) ++_destroy_all_sets(struct ip_set_net *inst) + { +- struct ip_set *set = container_of(head, struct ip_set, rcu); ++ struct ip_set *set; ++ ip_set_id_t i; ++ bool need_wait = false; + +- ip_set_destroy_set(set); ++ /* First cancel gc's: set:list sets are flushed as well */ ++ for (i = 0; i < inst->ip_set_max; i++) { ++ set = ip_set(inst, i); ++ if (set) { ++ set->variant->cancel_gc(set); ++ if (set->type->features & IPSET_TYPE_NAME) ++ need_wait = true; ++ } ++ } ++ /* Must wait for flush to be really finished */ ++ if (need_wait) ++ rcu_barrier(); ++ for (i = 0; i < inst->ip_set_max; i++) { ++ set = ip_set(inst, i); ++ if (set) { ++ ip_set(inst, i) = NULL; ++ set->variant->destroy(set); ++ module_put(set->type->me); ++ kfree(set); ++ } ++ } + } + + static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, +@@ -1204,11 +1231,10 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, + if (unlikely(protocol_min_failed(attr))) + return -IPSET_ERR_PROTOCOL; + +- + /* Commands are serialized and references are + * protected by the ip_set_ref_lock. + * External systems (i.e. xt_set) must call +- * ip_set_put|get_nfnl_* functions, that way we ++ * ip_set_nfnl_get_* functions, that way we + * can safely check references here. + * + * list:set timer can only decrement the reference +@@ -1216,8 +1242,6 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, + * without holding the lock. + */ + if (!attr[IPSET_ATTR_SETNAME]) { +- /* Must wait for flush to be really finished in list:set */ +- rcu_barrier(); + read_lock_bh(&ip_set_ref_lock); + for (i = 0; i < inst->ip_set_max; i++) { + s = ip_set(inst, i); +@@ -1228,15 +1252,7 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, + } + inst->is_destroyed = true; + read_unlock_bh(&ip_set_ref_lock); +- for (i = 0; i < inst->ip_set_max; i++) { +- s = ip_set(inst, i); +- if (s) { +- ip_set(inst, i) = NULL; +- /* Must cancel garbage collectors */ +- s->variant->cancel_gc(s); +- ip_set_destroy_set(s); +- } +- } ++ _destroy_all_sets(inst); + /* Modified by ip_set_destroy() only, which is serialized */ + inst->is_destroyed = false; + } else { +@@ -1257,12 +1273,12 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, + features = s->type->features; + ip_set(inst, i) = NULL; + read_unlock_bh(&ip_set_ref_lock); ++ /* Must cancel garbage collectors */ ++ s->variant->cancel_gc(s); + if (features & IPSET_TYPE_NAME) { + /* Must wait for flush to be really finished */ + rcu_barrier(); + } +- /* Must cancel garbage collectors */ +- s->variant->cancel_gc(s); + call_rcu(&s->rcu, ip_set_destroy_set_rcu); + } + return 0; +@@ -2367,30 +2383,25 @@ ip_set_net_init(struct net *net) + } + + static void __net_exit +-ip_set_net_exit(struct net *net) ++ip_set_net_pre_exit(struct net *net) + { + struct ip_set_net *inst = ip_set_pernet(net); + +- struct ip_set *set = NULL; +- ip_set_id_t i; +- + inst->is_deleted = true; /* flag for ip_set_nfnl_put */ ++} + +- nfnl_lock(NFNL_SUBSYS_IPSET); +- for (i = 0; i < inst->ip_set_max; i++) { +- set = ip_set(inst, i); +- if (set) { +- ip_set(inst, i) = NULL; +- set->variant->cancel_gc(set); +- ip_set_destroy_set(set); +- } +- } +- nfnl_unlock(NFNL_SUBSYS_IPSET); ++static void __net_exit ++ip_set_net_exit(struct net *net) ++{ ++ struct ip_set_net *inst = ip_set_pernet(net); ++ ++ _destroy_all_sets(inst); + kvfree(rcu_dereference_protected(inst->ip_set_list, 1)); + } + + static struct pernet_operations ip_set_net_ops = { + .init = ip_set_net_init, ++ .pre_exit = ip_set_net_pre_exit, + .exit = ip_set_net_exit, + .id = &ip_set_net_id, + .size = sizeof(struct ip_set_net), +diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c +index 6bc7019982b05..e839c356bcb56 100644 +--- a/net/netfilter/ipset/ip_set_list_set.c ++++ b/net/netfilter/ipset/ip_set_list_set.c +@@ -79,7 +79,7 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb, + struct set_elem *e; + int ret; + +- list_for_each_entry(e, &map->members, list) { ++ list_for_each_entry_rcu(e, &map->members, list) { + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, set))) + continue; +@@ -99,7 +99,7 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb, + struct set_elem *e; + int ret; + +- list_for_each_entry(e, &map->members, list) { ++ list_for_each_entry_rcu(e, &map->members, list) { + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, set))) + continue; +@@ -188,9 +188,10 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, + struct list_set *map = set->data; + struct set_adt_elem *d = value; + struct set_elem *e, *next, *prev = NULL; +- int ret; ++ int ret = 0; + +- list_for_each_entry(e, &map->members, list) { ++ rcu_read_lock(); ++ list_for_each_entry_rcu(e, &map->members, list) { + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, set))) + continue; +@@ -201,6 +202,7 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, + + if (d->before == 0) { + ret = 1; ++ goto out; + } else if (d->before > 0) { + next = list_next_entry(e, list); + ret = !list_is_last(&e->list, &map->members) && +@@ -208,9 +210,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, + } else { + ret = prev && prev->id == d->refid; + } +- return ret; ++ goto out; + } +- return 0; ++out: ++ rcu_read_unlock(); ++ return ret; + } + + static void +@@ -239,7 +243,7 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, + + /* Find where to add the new entry */ + n = prev = next = NULL; +- list_for_each_entry(e, &map->members, list) { ++ list_for_each_entry_rcu(e, &map->members, list) { + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, set))) + continue; +@@ -316,9 +320,9 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext, + { + struct list_set *map = set->data; + struct set_adt_elem *d = value; +- struct set_elem *e, *next, *prev = NULL; ++ struct set_elem *e, *n, *next, *prev = NULL; + +- list_for_each_entry(e, &map->members, list) { ++ list_for_each_entry_safe(e, n, &map->members, list) { + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, set))) + continue; +@@ -424,14 +428,8 @@ static void + list_set_destroy(struct ip_set *set) + { + struct list_set *map = set->data; +- struct set_elem *e, *n; + +- list_for_each_entry_safe(e, n, &map->members, list) { +- list_del(&e->list); +- ip_set_put_byindex(map->net, e->id); +- ip_set_ext_destroy(set, e); +- kfree(e); +- } ++ WARN_ON_ONCE(!list_empty(&map->members)); + kfree(map); + + set->data = NULL; +-- +2.43.0 + diff --git a/queue-6.1/netfilter-use-flowlabel-flow-key-when-re-routing-man.patch b/queue-6.1/netfilter-use-flowlabel-flow-key-when-re-routing-man.patch new file mode 100644 index 00000000000..39a84b7d874 --- /dev/null +++ b/queue-6.1/netfilter-use-flowlabel-flow-key-when-re-routing-man.patch @@ -0,0 +1,41 @@ +From e363822882ca60f0ded2432ba6f5feca284f5d0a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Jun 2024 12:23:31 +0200 +Subject: netfilter: Use flowlabel flow key when re-routing mangled packets + +From: Florian Westphal + +[ Upstream commit 6f8f132cc7bac2ac76911e47d5baa378aafda4cb ] + +'ip6 dscp set $v' in an nftables outpute route chain has no effect. +While nftables does detect the dscp change and calls the reroute hook. +But ip6_route_me_harder never sets the dscp/flowlabel: +flowlabel/dsfield routing rules are ignored and no reroute takes place. + +Thanks to Yi Chen for an excellent reproducer script that I used +to validate this change. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Reported-by: Yi Chen +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv6/netfilter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index 857713d7a38a5..1df23cd31b767 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -36,6 +36,7 @@ int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff + .flowi6_uid = sock_net_uid(net, sk), + .daddr = iph->daddr, + .saddr = iph->saddr, ++ .flowlabel = ip6_flowinfo(iph), + }; + int err; + +-- +2.43.0 + diff --git a/queue-6.1/nfs-add-barriers-when-testing-for-nfs_fsdata_blocked.patch b/queue-6.1/nfs-add-barriers-when-testing-for-nfs_fsdata_blocked.patch new file mode 100644 index 00000000000..361815e960e --- /dev/null +++ b/queue-6.1/nfs-add-barriers-when-testing-for-nfs_fsdata_blocked.patch @@ -0,0 +1,151 @@ +From 16a2702bbee17d76af374fd55d2262be68736275 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 May 2024 13:27:17 +1000 +Subject: NFS: add barriers when testing for NFS_FSDATA_BLOCKED + +From: NeilBrown + +[ Upstream commit 99bc9f2eb3f79a2b4296d9bf43153e1d10ca50d3 ] + +dentry->d_fsdata is set to NFS_FSDATA_BLOCKED while unlinking or +renaming-over a file to ensure that no open succeeds while the NFS +operation progressed on the server. + +Setting dentry->d_fsdata to NFS_FSDATA_BLOCKED is done under ->d_lock +after checking the refcount is not elevated. Any attempt to open the +file (through that name) will go through lookp_open() which will take +->d_lock while incrementing the refcount, we can be sure that once the +new value is set, __nfs_lookup_revalidate() *will* see the new value and +will block. + +We don't have any locking guarantee that when we set ->d_fsdata to NULL, +the wait_var_event() in __nfs_lookup_revalidate() will notice. +wait/wake primitives do NOT provide barriers to guarantee order. We +must use smp_load_acquire() in wait_var_event() to ensure we look at an +up-to-date value, and must use smp_store_release() before wake_up_var(). + +This patch adds those barrier functions and factors out +block_revalidate() and unblock_revalidate() far clarity. + +There is also a hypothetical bug in that if memory allocation fails +(which never happens in practice) we might leave ->d_fsdata locked. +This patch adds the missing call to unblock_revalidate(). + +Reported-and-tested-by: Richard Kojedzinszky +Closes: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1071501 +Fixes: 3c59366c207e ("NFS: don't unhash dentry during unlink/rename") +Signed-off-by: NeilBrown +Signed-off-by: Trond Myklebust +Signed-off-by: Sasha Levin +--- + fs/nfs/dir.c | 47 ++++++++++++++++++++++++++++++++--------------- + 1 file changed, 32 insertions(+), 15 deletions(-) + +diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c +index f594dac436a7e..a5a4d9422d6ed 100644 +--- a/fs/nfs/dir.c ++++ b/fs/nfs/dir.c +@@ -1792,9 +1792,10 @@ __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags, + if (parent != READ_ONCE(dentry->d_parent)) + return -ECHILD; + } else { +- /* Wait for unlink to complete */ ++ /* Wait for unlink to complete - see unblock_revalidate() */ + wait_var_event(&dentry->d_fsdata, +- dentry->d_fsdata != NFS_FSDATA_BLOCKED); ++ smp_load_acquire(&dentry->d_fsdata) ++ != NFS_FSDATA_BLOCKED); + parent = dget_parent(dentry); + ret = reval(d_inode(parent), dentry, flags); + dput(parent); +@@ -1807,6 +1808,29 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) + return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate); + } + ++static void block_revalidate(struct dentry *dentry) ++{ ++ /* old devname - just in case */ ++ kfree(dentry->d_fsdata); ++ ++ /* Any new reference that could lead to an open ++ * will take ->d_lock in lookup_open() -> d_lookup(). ++ * Holding this lock ensures we cannot race with ++ * __nfs_lookup_revalidate() and removes and need ++ * for further barriers. ++ */ ++ lockdep_assert_held(&dentry->d_lock); ++ ++ dentry->d_fsdata = NFS_FSDATA_BLOCKED; ++} ++ ++static void unblock_revalidate(struct dentry *dentry) ++{ ++ /* store_release ensures wait_var_event() sees the update */ ++ smp_store_release(&dentry->d_fsdata, NULL); ++ wake_up_var(&dentry->d_fsdata); ++} ++ + /* + * A weaker form of d_revalidate for revalidating just the d_inode(dentry) + * when we don't really care about the dentry name. This is called when a +@@ -2489,15 +2513,12 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry) + spin_unlock(&dentry->d_lock); + goto out; + } +- /* old devname */ +- kfree(dentry->d_fsdata); +- dentry->d_fsdata = NFS_FSDATA_BLOCKED; ++ block_revalidate(dentry); + + spin_unlock(&dentry->d_lock); + error = nfs_safe_remove(dentry); + nfs_dentry_remove_handle_error(dir, dentry, error); +- dentry->d_fsdata = NULL; +- wake_up_var(&dentry->d_fsdata); ++ unblock_revalidate(dentry); + out: + trace_nfs_unlink_exit(dir, dentry, error); + return error; +@@ -2609,8 +2630,7 @@ nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data) + { + struct dentry *new_dentry = data->new_dentry; + +- new_dentry->d_fsdata = NULL; +- wake_up_var(&new_dentry->d_fsdata); ++ unblock_revalidate(new_dentry); + } + + /* +@@ -2672,11 +2692,6 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + if (WARN_ON(new_dentry->d_flags & DCACHE_NFSFS_RENAMED) || + WARN_ON(new_dentry->d_fsdata == NFS_FSDATA_BLOCKED)) + goto out; +- if (new_dentry->d_fsdata) { +- /* old devname */ +- kfree(new_dentry->d_fsdata); +- new_dentry->d_fsdata = NULL; +- } + + spin_lock(&new_dentry->d_lock); + if (d_count(new_dentry) > 2) { +@@ -2698,7 +2713,7 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + new_dentry = dentry; + new_inode = NULL; + } else { +- new_dentry->d_fsdata = NFS_FSDATA_BLOCKED; ++ block_revalidate(new_dentry); + must_unblock = true; + spin_unlock(&new_dentry->d_lock); + } +@@ -2710,6 +2725,8 @@ int nfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, + must_unblock ? nfs_unblock_rename : NULL); + if (IS_ERR(task)) { ++ if (must_unblock) ++ unblock_revalidate(new_dentry); + error = PTR_ERR(task); + goto out; + } +-- +2.43.0 + diff --git a/queue-6.1/nfsv4.1-enforce-rootpath-check-in-fs_location-query.patch b/queue-6.1/nfsv4.1-enforce-rootpath-check-in-fs_location-query.patch new file mode 100644 index 00000000000..e3b16f024c7 --- /dev/null +++ b/queue-6.1/nfsv4.1-enforce-rootpath-check-in-fs_location-query.patch @@ -0,0 +1,75 @@ +From bc1152b4565235786cc41edbc645f08a1eff041f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 May 2024 15:44:35 -0400 +Subject: NFSv4.1 enforce rootpath check in fs_location query + +From: Olga Kornievskaia + +[ Upstream commit 28568c906c1bb5f7560e18082ed7d6295860f1c2 ] + +In commit 4ca9f31a2be66 ("NFSv4.1 test and add 4.1 trunking transport"), +we introduce the ability to query the NFS server for possible trunking +locations of the existing filesystem. However, we never checked the +returned file system path for these alternative locations. According +to the RFC, the server can say that the filesystem currently known +under "fs_root" of fs_location also resides under these server +locations under the following "rootpath" pathname. The client cannot +handle trunking a filesystem that reside under different location +under different paths other than what the main path is. This patch +enforces the check that fs_root path and rootpath path in fs_location +reply is the same. + +Fixes: 4ca9f31a2be6 ("NFSv4.1 test and add 4.1 trunking transport") +Signed-off-by: Olga Kornievskaia +Signed-off-by: Trond Myklebust +Signed-off-by: Sasha Levin +--- + fs/nfs/nfs4proc.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c +index bda3050817c90..ec641a8f6604b 100644 +--- a/fs/nfs/nfs4proc.c ++++ b/fs/nfs/nfs4proc.c +@@ -4009,6 +4009,23 @@ static void test_fs_location_for_trunking(struct nfs4_fs_location *location, + } + } + ++static bool _is_same_nfs4_pathname(struct nfs4_pathname *path1, ++ struct nfs4_pathname *path2) ++{ ++ int i; ++ ++ if (path1->ncomponents != path2->ncomponents) ++ return false; ++ for (i = 0; i < path1->ncomponents; i++) { ++ if (path1->components[i].len != path2->components[i].len) ++ return false; ++ if (memcmp(path1->components[i].data, path2->components[i].data, ++ path1->components[i].len)) ++ return false; ++ } ++ return true; ++} ++ + static int _nfs4_discover_trunking(struct nfs_server *server, + struct nfs_fh *fhandle) + { +@@ -4042,9 +4059,13 @@ static int _nfs4_discover_trunking(struct nfs_server *server, + if (status) + goto out_free_3; + +- for (i = 0; i < locations->nlocations; i++) ++ for (i = 0; i < locations->nlocations; i++) { ++ if (!_is_same_nfs4_pathname(&locations->fs_path, ++ &locations->locations[i].rootpath)) ++ continue; + test_fs_location_for_trunking(&locations->locations[i], clp, + server); ++ } + out_free_3: + kfree(locations->fattr); + out_free_2: +-- +2.43.0 + diff --git a/queue-6.1/nvmet-passthru-propagate-status-from-id-override-fun.patch b/queue-6.1/nvmet-passthru-propagate-status-from-id-override-fun.patch new file mode 100644 index 00000000000..2e8a868685c --- /dev/null +++ b/queue-6.1/nvmet-passthru-propagate-status-from-id-override-fun.patch @@ -0,0 +1,46 @@ +From e92b7364cb1b56796b20917d3dc4879e400488e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 12 Jun 2024 16:02:40 +0200 +Subject: nvmet-passthru: propagate status from id override functions + +From: Daniel Wagner + +[ Upstream commit d76584e53f4244dbc154bec447c3852600acc914 ] + +The id override functions return a status which is not propagated to the +caller. + +Fixes: c1fef73f793b ("nvmet: add passthru code to process commands") +Signed-off-by: Daniel Wagner +Reviewed-by: Chaitanya Kulkarni +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/passthru.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c +index a0a292d49588c..dc756a1c9d0e3 100644 +--- a/drivers/nvme/target/passthru.c ++++ b/drivers/nvme/target/passthru.c +@@ -226,13 +226,13 @@ static void nvmet_passthru_execute_cmd_work(struct work_struct *w) + req->cmd->common.opcode == nvme_admin_identify) { + switch (req->cmd->identify.cns) { + case NVME_ID_CNS_CTRL: +- nvmet_passthru_override_id_ctrl(req); ++ status = nvmet_passthru_override_id_ctrl(req); + break; + case NVME_ID_CNS_NS: +- nvmet_passthru_override_id_ns(req); ++ status = nvmet_passthru_override_id_ns(req); + break; + case NVME_ID_CNS_NS_DESC_LIST: +- nvmet_passthru_override_id_descs(req); ++ status = nvmet_passthru_override_id_descs(req); + break; + } + } else if (status < 0) +-- +2.43.0 + diff --git a/queue-6.1/platform-x86-dell-smbios-fix-wrong-token-data-in-sys.patch b/queue-6.1/platform-x86-dell-smbios-fix-wrong-token-data-in-sys.patch new file mode 100644 index 00000000000..fe04761c650 --- /dev/null +++ b/queue-6.1/platform-x86-dell-smbios-fix-wrong-token-data-in-sys.patch @@ -0,0 +1,226 @@ +From eaf2a14459ca5aa8e1523355a1756e9b41951ea0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 May 2024 22:49:02 +0200 +Subject: platform/x86: dell-smbios: Fix wrong token data in sysfs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Armin Wolf + +[ Upstream commit 1981b296f858010eae409548fd297659b2cc570e ] + +When reading token data from sysfs on my Inspiron 3505, the token +locations and values are wrong. This happens because match_attribute() +blindly assumes that all entries in da_tokens have an associated +entry in token_attrs. + +This however is not true as soon as da_tokens[] contains zeroed +token entries. Those entries are being skipped when initialising +token_attrs, breaking the core assumption of match_attribute(). + +Fix this by defining an extra struct for each pair of token attributes +and use container_of() to retrieve token information. + +Tested on a Dell Inspiron 3050. + +Fixes: 33b9ca1e53b4 ("platform/x86: dell-smbios: Add a sysfs interface for SMBIOS tokens") +Signed-off-by: Armin Wolf +Reviewed-by: Ilpo Järvinen +Link: https://lore.kernel.org/r/20240528204903.445546-1-W_Armin@gmx.de +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/dell/dell-smbios-base.c | 92 ++++++++------------ + 1 file changed, 36 insertions(+), 56 deletions(-) + +diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c +index e61bfaf8b5c48..86b95206cb1bd 100644 +--- a/drivers/platform/x86/dell/dell-smbios-base.c ++++ b/drivers/platform/x86/dell/dell-smbios-base.c +@@ -11,6 +11,7 @@ + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -25,11 +26,16 @@ static u32 da_supported_commands; + static int da_num_tokens; + static struct platform_device *platform_device; + static struct calling_interface_token *da_tokens; +-static struct device_attribute *token_location_attrs; +-static struct device_attribute *token_value_attrs; ++static struct token_sysfs_data *token_entries; + static struct attribute **token_attrs; + static DEFINE_MUTEX(smbios_mutex); + ++struct token_sysfs_data { ++ struct device_attribute location_attr; ++ struct device_attribute value_attr; ++ struct calling_interface_token *token; ++}; ++ + struct smbios_device { + struct list_head list; + struct device *device; +@@ -416,47 +422,26 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) + } + } + +-static int match_attribute(struct device *dev, +- struct device_attribute *attr) +-{ +- int i; +- +- for (i = 0; i < da_num_tokens * 2; i++) { +- if (!token_attrs[i]) +- continue; +- if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) +- return i/2; +- } +- dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); +- return -EINVAL; +-} +- + static ssize_t location_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +- int i; ++ struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- i = match_attribute(dev, attr); +- if (i > 0) +- return sysfs_emit(buf, "%08x", da_tokens[i].location); +- return 0; ++ return sysfs_emit(buf, "%08x", data->token->location); + } + + static ssize_t value_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +- int i; ++ struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + +- i = match_attribute(dev, attr); +- if (i > 0) +- return sysfs_emit(buf, "%08x", da_tokens[i].value); +- return 0; ++ return sysfs_emit(buf, "%08x", data->token->value); + } + + static struct attribute_group smbios_attribute_group = { +@@ -473,22 +458,15 @@ static int build_tokens_sysfs(struct platform_device *dev) + { + char *location_name; + char *value_name; +- size_t size; + int ret; + int i, j; + +- /* (number of tokens + 1 for null terminated */ +- size = sizeof(struct device_attribute) * (da_num_tokens + 1); +- token_location_attrs = kzalloc(size, GFP_KERNEL); +- if (!token_location_attrs) ++ token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL); ++ if (!token_entries) + return -ENOMEM; +- token_value_attrs = kzalloc(size, GFP_KERNEL); +- if (!token_value_attrs) +- goto out_allocate_value; + + /* need to store both location and value + terminator*/ +- size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); +- token_attrs = kzalloc(size, GFP_KERNEL); ++ token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL); + if (!token_attrs) + goto out_allocate_attrs; + +@@ -496,27 +474,32 @@ static int build_tokens_sysfs(struct platform_device *dev) + /* skip empty */ + if (da_tokens[i].tokenID == 0) + continue; ++ ++ token_entries[i].token = &da_tokens[i]; ++ + /* add location */ + location_name = kasprintf(GFP_KERNEL, "%04x_location", + da_tokens[i].tokenID); + if (location_name == NULL) + goto out_unwind_strings; +- sysfs_attr_init(&token_location_attrs[i].attr); +- token_location_attrs[i].attr.name = location_name; +- token_location_attrs[i].attr.mode = 0444; +- token_location_attrs[i].show = location_show; +- token_attrs[j++] = &token_location_attrs[i].attr; ++ ++ sysfs_attr_init(&token_entries[i].location_attr.attr); ++ token_entries[i].location_attr.attr.name = location_name; ++ token_entries[i].location_attr.attr.mode = 0444; ++ token_entries[i].location_attr.show = location_show; ++ token_attrs[j++] = &token_entries[i].location_attr.attr; + + /* add value */ + value_name = kasprintf(GFP_KERNEL, "%04x_value", + da_tokens[i].tokenID); + if (value_name == NULL) + goto loop_fail_create_value; +- sysfs_attr_init(&token_value_attrs[i].attr); +- token_value_attrs[i].attr.name = value_name; +- token_value_attrs[i].attr.mode = 0444; +- token_value_attrs[i].show = value_show; +- token_attrs[j++] = &token_value_attrs[i].attr; ++ ++ sysfs_attr_init(&token_entries[i].value_attr.attr); ++ token_entries[i].value_attr.attr.name = value_name; ++ token_entries[i].value_attr.attr.mode = 0444; ++ token_entries[i].value_attr.show = value_show; ++ token_attrs[j++] = &token_entries[i].value_attr.attr; + continue; + + loop_fail_create_value: +@@ -532,14 +515,12 @@ static int build_tokens_sysfs(struct platform_device *dev) + + out_unwind_strings: + while (i--) { +- kfree(token_location_attrs[i].attr.name); +- kfree(token_value_attrs[i].attr.name); ++ kfree(token_entries[i].location_attr.attr.name); ++ kfree(token_entries[i].value_attr.attr.name); + } + kfree(token_attrs); + out_allocate_attrs: +- kfree(token_value_attrs); +-out_allocate_value: +- kfree(token_location_attrs); ++ kfree(token_entries); + + return -ENOMEM; + } +@@ -551,12 +532,11 @@ static void free_group(struct platform_device *pdev) + sysfs_remove_group(&pdev->dev.kobj, + &smbios_attribute_group); + for (i = 0; i < da_num_tokens; i++) { +- kfree(token_location_attrs[i].attr.name); +- kfree(token_value_attrs[i].attr.name); ++ kfree(token_entries[i].location_attr.attr.name); ++ kfree(token_entries[i].value_attr.attr.name); + } + kfree(token_attrs); +- kfree(token_value_attrs); +- kfree(token_location_attrs); ++ kfree(token_entries); + } + + static int __init dell_smbios_init(void) +-- +2.43.0 + diff --git a/queue-6.1/selftests-ftrace-fix-to-check-required-event-file.patch b/queue-6.1/selftests-ftrace-fix-to-check-required-event-file.patch new file mode 100644 index 00000000000..994c74e9156 --- /dev/null +++ b/queue-6.1/selftests-ftrace-fix-to-check-required-event-file.patch @@ -0,0 +1,40 @@ +From 07c1d5f64ab8eea0c646c22d76f762af3f0035cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 May 2024 09:00:22 +0900 +Subject: selftests/ftrace: Fix to check required event file + +From: Masami Hiramatsu (Google) + +[ Upstream commit f6c3c83db1d939ebdb8c8922748ae647d8126d91 ] + +The dynevent/test_duplicates.tc test case uses `syscalls/sys_enter_openat` +event for defining eprobe on it. Since this `syscalls` events depend on +CONFIG_FTRACE_SYSCALLS=y, if it is not set, the test will fail. + +Add the event file to `required` line so that the test will return +`unsupported` result. + +Fixes: 297e1dcdca3d ("selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes") +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + .../testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc +index d3a79da215c8b..5f72abe6fa79b 100644 +--- a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc ++++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc +@@ -1,7 +1,7 @@ + #!/bin/sh + # SPDX-License-Identifier: GPL-2.0 + # description: Generic dynamic event - check if duplicate events are caught +-# requires: dynamic_events "e[:[/][]] . []":README ++# requires: dynamic_events "e[:[/][]] . []":README events/syscalls/sys_enter_openat + + echo 0 > events/enable + +-- +2.43.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 9bd925468e8..0c7618ec3a4 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -104,3 +104,59 @@ scsi-sd-use-read-16-when-reading-block-zero-on-large-capacity-disks.patch gve-clear-napi-skb-before-dev_kfree_skb_any.patch powerpc-uaccess-fix-build-errors-seen-with-gcc-13-14.patch input-try-trimming-too-long-modalias-strings.patch +cxl-test-add-missing-vmalloc.h-for-tools-testing-cxl.patch +cachefiles-add-output-string-to-cachefiles_obj_-get-.patch +cachefiles-remove-requests-from-xarray-during-flushi.patch +cachefiles-introduce-object-ondemand-state.patch +cachefiles-extract-ondemand-info-field-from-cachefil.patch +cachefiles-resend-an-open-request-if-the-read-reques.patch +cachefiles-add-spin_lock-for-cachefiles_ondemand_inf.patch +cachefiles-add-restore-command-to-recover-inflight-o.patch +cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch +cachefiles-fix-slab-use-after-free-in-cachefiles_ond.patch-24352 +cachefiles-remove-err_put_fd-label-in-cachefiles_ond.patch +cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch +cachefiles-defer-exposing-anon_fd-until-after-copy_t.patch +cachefiles-flush-all-requests-after-setting-cachefil.patch +selftests-ftrace-fix-to-check-required-event-file.patch +clk-sifive-do-not-register-clkdevs-for-prci-clocks.patch +nfsv4.1-enforce-rootpath-check-in-fs_location-query.patch +sunrpc-return-proper-error-from-gss_wrap_req_priv.patch +nfs-add-barriers-when-testing-for-nfs_fsdata_blocked.patch +platform-x86-dell-smbios-fix-wrong-token-data-in-sys.patch +gpio-tqmx86-fix-typo-in-kconfig-label.patch +gpio-tqmx86-remove-unneeded-call-to-platform_set_drv.patch +gpio-tqmx86-introduce-shadow-register-for-gpio-outpu.patch +gpio-tqmx86-convert-to-immutable-irq_chip.patch +gpio-tqmx86-store-irq-trigger-type-and-unmask-status.patch +gpio-tqmx86-fix-broken-irq_type_edge_both-interrupt-.patch +hid-core-remove-unnecessary-warn_on-in-implement.patch +iommu-amd-fix-sysfs-leak-in-iommu-init.patch +hid-logitech-dj-fix-memory-leak-in-logi_dj_recv_swit.patch +drm-vmwgfx-port-the-framebuffer-code-to-drm-fb-helpe.patch +drm-vmwgfx-refactor-drm-connector-probing-for-displa.patch +drm-vmwgfx-filter-modes-which-exceed-graphics-memory.patch +drm-vmwgfx-3d-disabled-should-not-effect-stdu-memory.patch +drm-vmwgfx-remove-stdu-logic-from-generic-mode_valid.patch +net-sfp-always-call-sfp_sm_mod_remove-on-remove.patch +net-hns3-fix-kernel-crash-problem-in-concurrent-scen.patch +net-hns3-add-cond_resched-to-hns3-ring-buffer-init-p.patch +liquidio-adjust-a-null-pointer-handling-path-in-lio_.patch +drm-komeda-check-for-error-valued-pointer.patch +drm-bridge-panel-fix-runtime-warning-on-panel-bridge.patch +tcp-fix-race-in-tcp_v6_syn_recv_sock.patch +geneve-fix-incorrect-inner-network-header-offset-whe.patch +net-mlx5e-fix-features-validation-check-for-tunneled.patch +bluetooth-l2cap-fix-rejecting-l2cap_conn_param_updat.patch +netfilter-ipset-fix-race-between-namespace-cleanup-a.patch +netfilter-use-flowlabel-flow-key-when-re-routing-man.patch +net-pse-pd-use-eopnotsupp-error-code-instead-of-enot.patch +gve-ignore-nonrelevant-gso-type-bits-when-processing.patch +net-stmmac-replace-priv-speed-with-the-porttransmitr.patch +nvmet-passthru-propagate-status-from-id-override-fun.patch +net-ipv6-fix-the-rt-cache-flush-via-sysctl-using-a-p.patch +net-bridge-mst-pass-vlan-group-directly-to-br_mst_vl.patch +net-bridge-mst-fix-suspicious-rcu-usage-in-br_mst_se.patch +ionic-fix-use-after-netif_napi_del.patch +af_unix-read-with-msg_peek-loops-if-the-first-unread.patch +bnxt_en-adjust-logging-of-firmware-messages-in-case-.patch diff --git a/queue-6.1/sunrpc-return-proper-error-from-gss_wrap_req_priv.patch b/queue-6.1/sunrpc-return-proper-error-from-gss_wrap_req_priv.patch new file mode 100644 index 00000000000..afc229999bf --- /dev/null +++ b/queue-6.1/sunrpc-return-proper-error-from-gss_wrap_req_priv.patch @@ -0,0 +1,40 @@ +From 30238f3eaf011530329a66e7ff0a2e941d8092ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 May 2024 16:47:16 +0800 +Subject: SUNRPC: return proper error from gss_wrap_req_priv + +From: Chen Hanxiao + +[ Upstream commit 33c94d7e3cb84f6d130678d6d59ba475a6c489cf ] + +don't return 0 if snd_buf->len really greater than snd_buf->buflen + +Signed-off-by: Chen Hanxiao +Fixes: 0c77668ddb4e ("SUNRPC: Introduce trace points in rpc_auth_gss.ko") +Reviewed-by: Benjamin Coddington +Reviewed-by: Chuck Lever +Signed-off-by: Trond Myklebust +Signed-off-by: Sasha Levin +--- + net/sunrpc/auth_gss/auth_gss.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c +index 2d7b1e03110ae..3ef511d7af190 100644 +--- a/net/sunrpc/auth_gss/auth_gss.c ++++ b/net/sunrpc/auth_gss/auth_gss.c +@@ -1858,8 +1858,10 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; + maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); + /* slack space should prevent this ever happening: */ +- if (unlikely(snd_buf->len > snd_buf->buflen)) ++ if (unlikely(snd_buf->len > snd_buf->buflen)) { ++ status = -EIO; + goto wrap_failed; ++ } + /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was + * done anyway, so it's safe to put the request on the wire: */ + if (maj_stat == GSS_S_CONTEXT_EXPIRED) +-- +2.43.0 + diff --git a/queue-6.1/tcp-fix-race-in-tcp_v6_syn_recv_sock.patch b/queue-6.1/tcp-fix-race-in-tcp_v6_syn_recv_sock.patch new file mode 100644 index 00000000000..0fd70e2f9f9 --- /dev/null +++ b/queue-6.1/tcp-fix-race-in-tcp_v6_syn_recv_sock.patch @@ -0,0 +1,54 @@ +From 99b75c373db7c89f26c26e3ccdfc87670cad2e7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Jun 2024 15:46:51 +0000 +Subject: tcp: fix race in tcp_v6_syn_recv_sock() + +From: Eric Dumazet + +[ Upstream commit d37fe4255abe8e7b419b90c5847e8ec2b8debb08 ] + +tcp_v6_syn_recv_sock() calls ip6_dst_store() before +inet_sk(newsk)->pinet6 has been set up. + +This means ip6_dst_store() writes over the parent (listener) +np->dst_cookie. + +This is racy because multiple threads could share the same +parent and their final np->dst_cookie could be wrong. + +Move ip6_dst_store() call after inet_sk(newsk)->pinet6 +has been changed and after the copy of parent ipv6_pinfo. + +Fixes: e994b2f0fb92 ("tcp: do not lock listener to process SYN packets") +Signed-off-by: Eric Dumazet +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/ipv6/tcp_ipv6.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index ba9a22db5805c..4b0e05349862d 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -1291,7 +1291,6 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * + */ + + newsk->sk_gso_type = SKB_GSO_TCPV6; +- ip6_dst_store(newsk, dst, NULL, NULL); + inet6_sk_rx_dst_set(newsk, skb); + + inet_sk(newsk)->pinet6 = tcp_inet6_sk(newsk); +@@ -1302,6 +1301,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * + + memcpy(newnp, np, sizeof(struct ipv6_pinfo)); + ++ ip6_dst_store(newsk, dst, NULL, NULL); ++ + newsk->sk_v6_daddr = ireq->ir_v6_rmt_addr; + newnp->saddr = ireq->ir_v6_loc_addr; + newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr; +-- +2.43.0 + -- 2.47.3