--- /dev/null
+From 645ccb61deb253d70b672e41de5c336946de151d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <Rao.Shoaib@oracle.com>
+
+[ 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 <Rao.Shoaib@oracle.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Link: https://lore.kernel.org/r/20240611084639.2248934-1-Rao.Shoaib@oracle.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7ad55ecff9b7b4c98082b48e6fdcaa76c099ff41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 May 2024 16:03:07 -0400
+Subject: Bluetooth: L2CAP: Fix rejecting L2CAP_CONN_PARAM_UPDATE_REQ
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ 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 <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2b1acec911c8ec1d2c63842b353c633d15331f96 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <amishin@t-argos.ru>
+
+[ 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 <michael.chan@broadcom.com>
+Signed-off-by: Aleksandr Mishin <amishin@t-argos.ru>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Reviewed-by: Michael Chan <michael.chan@broadcom.com>
+Link: https://lore.kernel.org/r/20240611082547.12178-1-amishin@t-argos.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 21f13c1c048f6d50789498512f5280f70d6e5e41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:42:57 +0800
+Subject: cachefiles: add output string to cachefiles_obj_[get|put]_ondemand_fd
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-2-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b6f72f3b9933d594808a7923eb2c8f1f040954dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Nov 2023 12:14:22 +0800
+Subject: cachefiles: add restore command to recover inflight ondemand read
+ requests
+
+From: Jia Zhu <zhujia.zj@bytedance.com>
+
+[ 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 <hsiangkao@linux.alibaba.com>
+Signed-off-by: Jia Zhu <zhujia.zj@bytedance.com>
+Signed-off-by: Xin Yin <yinxin.x@bytedance.com>
+Link: https://lore.kernel.org/r/20231120041422.75170-6-zhujia.zj@bytedance.com
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6e3d5571cc765e98e737c13858d48fe33f37bfd4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:43:03 +0800
+Subject: cachefiles: add spin_lock for cachefiles_ondemand_info
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-8-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 29d16a170159a19259e8c6dfa8bec1fbf40e55d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:43:05 +0800
+Subject: cachefiles: defer exposing anon_fd until after copy_to_user()
+ succeeds
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <houtao1@huawei.com>
+Signed-off-by: Baokun Li <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-10-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/uio.h>
+ #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
+
--- /dev/null
+From 446de368ae6891cbc249582539c7cc47f80378ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Nov 2023 12:14:19 +0800
+Subject: cachefiles: extract ondemand info field from cachefiles_object
+
+From: Jia Zhu <zhujia.zj@bytedance.com>
+
+[ 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 <zhujia.zj@bytedance.com>
+Link: https://lore.kernel.org/r/20231120041422.75170-3-zhujia.zj@bytedance.com
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8e3ca771693769a98994081151d0695e7b0f05b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:42:59 +0800
+Subject: cachefiles: fix slab-use-after-free in cachefiles_ondemand_get_fd()
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <houtao1@huawei.com>
+Signed-off-by: Baokun Li <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-4-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Jia Zhu <zhujia.zj@bytedance.com>
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/uio.h>
+ #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
+
--- /dev/null
+From 209b422248ee51b76d4dab06cba3be99267585f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:43:00 +0800
+Subject: cachefiles: fix slab-use-after-free in
+ cachefiles_ondemand_daemon_read()
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-5-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Jia Zhu <zhujia.zj@bytedance.com>
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6ce77c57874159044b8cf4757076decb71b70e78 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:43:07 +0800
+Subject: cachefiles: flush all requests after setting CACHEFILES_DEAD
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-12-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 937e1b54b146fba50bf73830e327e0c8c8b3a4ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Nov 2023 12:14:18 +0800
+Subject: cachefiles: introduce object ondemand state
+
+From: Jia Zhu <zhujia.zj@bytedance.com>
+
+[ 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 <zhujia.zj@bytedance.com>
+Link: https://lore.kernel.org/r/20231120041422.75170-2-zhujia.zj@bytedance.com
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 52291051523282e31ebaeb56ac7bb61066a6f1a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-9-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 98eac6377ef752bd500028f35adb7b06d696209b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:43:01 +0800
+Subject: cachefiles: remove err_put_fd label in
+ cachefiles_ondemand_daemon_read()
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-6-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Jia Zhu <zhujia.zj@bytedance.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2b56c265a6133d0915651e8c342f7a8b873c0682 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 May 2024 19:42:58 +0800
+Subject: cachefiles: remove requests from xarray during flushing requests
+
+From: Baokun Li <libaokun1@huawei.com>
+
+[ 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 <libaokun1@huawei.com>
+Link: https://lore.kernel.org/r/20240522114308.2402121-3-libaokun@huaweicloud.com
+Acked-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Jia Zhu <zhujia.zj@bytedance.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6cb329b030cac9e0ecf361202eb2aa996ad7a7fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <zhujia.zj@bytedance.com>
+
+[ 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 <zhujia.zj@bytedance.com>
+Link: https://lore.kernel.org/r/20231120041422.75170-4-zhujia.zj@bytedance.com
+Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 0a790040838c ("cachefiles: add spin_lock for cachefiles_ondemand_info")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5677d68be93306c1f9e54c183e129cd1c05817e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 May 2024 17:14:12 -0700
+Subject: clk: sifive: Do not register clkdevs for PRCI clocks
+
+From: Samuel Holland <samuel.holland@sifive.com>
+
+[ 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 <linux@roeck-us.net>
+Closes: https://lore.kernel.org/linux-clk/7eda7621-0dde-4153-89e4-172e4c095d01@roeck-us.net/
+Suggested-by: Russell King <linux@armlinux.org.uk>
+Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
+Link: https://lore.kernel.org/r/20240528001432.1200403-1-samuel.holland@sifive.com
+Signed-off-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/clkdev.h>
+ #include <linux/delay.h>
+ #include <linux/io.h>
+ #include <linux/of_device.h>
+@@ -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
+
--- /dev/null
+From 4812a4a59816d53400938543e3767f4855d94d23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dave.jiang@intel.com>
+
+[ 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 <dan.j.williams@intel.com>
+Reviewed-by: Alison Schofield <alison.schofield@intel.com>
+Link: https://lore.kernel.org/r/20240528225551.1025977-1-dave.jiang@intel.com
+Signed-off-by: Dave Jiang <dave.jiang@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/platform_device.h>
+ #include <linux/mod_devicetable.h>
++#include <linux/vmalloc.h>
+ #include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/sizes.h>
+--
+2.43.0
+
--- /dev/null
+From 51dc931b01bc7a35e66044e539b02fc16b578e44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 10 Jun 2024 11:27:39 +0100
+Subject: drm/bridge/panel: Fix runtime warning on panel bridge release
+
+From: Adam Miotk <adam.miotk@arm.com>
+
+[ 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 <adam.miotk@arm.com>
+Signed-off-by: Maxime Ripard <mripard@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240610102739.139852-1-adam.miotk@arm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f58642e0963ce332f8335e47f6133be6adb689c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 10 Jun 2024 11:20:56 +0100
+Subject: drm/komeda: check for error-valued pointer
+
+From: Amjad Ouled-Ameur <amjad.ouled-ameur@arm.com>
+
+[ 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 <amjad.ouled-ameur@arm.com>
+Signed-off-by: Maxime Ripard <mripard@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240610102056.40406-1-amjad.ouled-ameur@arm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2aad74ed49bac4bc7c9d1385951ea099e8aad0d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 May 2024 13:47:18 -0500
+Subject: drm/vmwgfx: 3D disabled should not effect STDU memory limits
+
+From: Ian Forbes <ian.forbes@broadcom.com>
+
+[ 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 <ian.forbes@broadcom.com>
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-3-ian.forbes@broadcom.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4aa6e2c94091f9159f9829e16a3d020dce2cd325 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 May 2024 13:47:17 -0500
+Subject: drm/vmwgfx: Filter modes which exceed graphics memory
+
+From: Ian Forbes <ian.forbes@broadcom.com>
+
+[ 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 <ian.forbes@broadcom.com>
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-2-ian.forbes@broadcom.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4ff94900f302b23052678fa4e2437c688ae4eafd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 22 Oct 2022 00:02:32 -0400
+Subject: drm/vmwgfx: Port the framebuffer code to drm fb helpers
+
+From: Zack Rusin <zackr@vmware.com>
+
+[ 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 <zackr@vmware.com>
+Reviewed-by: Maaz Mombasawala <mombasawalam@vmware.com>
+Reviewed-by: Martin Krastev <krastevm@vmware.com>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+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 <sashal@kernel.org>
+---
+ 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 <drm/drm_aperture.h>
+ #include <drm/drm_drv.h>
++#include <drm/drm_fb_helper.h>
+ #include <drm/drm_gem_ttm_helper.h>
+ #include <drm/drm_ioctl.h>
+ #include <drm/drm_module.h>
+@@ -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 <linux/fb.h>
+-#include <linux/pci.h>
+-
+-#include <drm/drm_fourcc.h>
+-#include <drm/ttm/ttm_placement.h>
+-
+-#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
+
--- /dev/null
+From 695b16d84a82d773c1257b50ea80957ace4e6fff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jan 2024 15:08:00 -0500
+Subject: drm/vmwgfx: Refactor drm connector probing for display modes
+
+From: Martin Krastev <martin.krastev@broadcom.com>
+
+[ 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 <martin.krastev@broadcom.com>
+Reviewed-by: Zack Rusin <zack.rusin@broadcom.com>
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+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 <sashal@kernel.org>
+---
+ 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 <drm/drm_fourcc.h>
+ #include <drm/drm_rect.h>
+ #include <drm/drm_sysfs.h>
++#include <drm/drm_edid.h>
+
+ #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
+
--- /dev/null
+From 08d024c4be883d839b051471aae1e3b7e50512ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 May 2024 13:47:19 -0500
+Subject: drm/vmwgfx: Remove STDU logic from generic mode_valid function
+
+From: Ian Forbes <ian.forbes@broadcom.com>
+
+[ 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 <ian.forbes@broadcom.com>
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-4-ian.forbes@broadcom.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c413cb476ab32d7b022a4abbad82c80c068e3e19 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Jun 2024 23:32:48 +0300
+Subject: geneve: Fix incorrect inner network header offset when
+ innerprotoinherit is set
+
+From: Gal Pressman <gal@nvidia.com>
+
+[ 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 <gal@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eff14fc5f545f83b3d33f3cbd40117ec03fc5e2a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Mar 2023 10:55:12 +0100
+Subject: gpio: tqmx86: Convert to immutable irq_chip
+
+From: Linus Walleij <linus.walleij@linaro.org>
+
+[ Upstream commit 8e43827b6ae727a745ce7a8cc19184b28905a965 ]
+
+Convert the driver to immutable irq-chip with a bit of
+intuition.
+
+Cc: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 08af509efdf8 ("gpio: tqmx86: store IRQ trigger type and unmask status separately")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
+ #include <linux/slab.h>
+
+ #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
+
--- /dev/null
+From e6a70c4a2544d6ed34522ddcdb6315d366385972 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 May 2024 12:20:02 +0200
+Subject: gpio: tqmx86: fix broken IRQ_TYPE_EDGE_BOTH interrupt type
+
+From: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
+
+[ 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 <gregor.herburger@tq-group.com>
+Signed-off-by: Gregor Herburger <gregor.herburger@tq-group.com>
+Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
+Link: https://lore.kernel.org/r/515324f0491c4d44f4ef49f170354aca002d81ef.1717063994.git.matthias.schiffer@ew.tq-group.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5949da5f653b399a4267e448be6add9ac97c066a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 May 2024 12:19:59 +0200
+Subject: gpio: tqmx86: fix typo in Kconfig label
+
+From: Gregor Herburger <gregor.herburger@tq-group.com>
+
+[ 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 <gregor.herburger@tq-group.com>
+Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/e0e38c9944ad6d281d9a662a45d289b88edc808e.1717063994.git.matthias.schiffer@ew.tq-group.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 692a3adb85bcc1466919f572da79afa725725a7e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 May 2024 12:20:00 +0200
+Subject: gpio: tqmx86: introduce shadow register for GPIO output value
+
+From: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
+
+[ 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 <matthias.schiffer@ew.tq-group.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/d0555933becd45fa92a85675d26e4d59343ddc01.1717063994.git.matthias.schiffer@ew.tq-group.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <vvlasov@dev.rtsoft.ru>
+ */
+
++#include <linux/bitmap.h>
+ #include <linux/bitops.h>
+ #include <linux/errno.h>
+ #include <linux/gpio/driver.h>
+@@ -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
+
--- /dev/null
+From d85ec7f519b347a06f19f93144a373b8d7efab42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 1 Aug 2023 23:38:39 +0300
+Subject: gpio: tqmx86: remove unneeded call to platform_set_drvdata()
+
+From: Andrei Coardos <aboutphysycs@gmail.com>
+
+[ 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 <alex@shruggie.ro>
+Signed-off-by: Andrei Coardos <aboutphysycs@gmail.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 9d6a811b522b ("gpio: tqmx86: introduce shadow register for GPIO output value")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e8bdb9754b7ff1086390e6258dabf840bb31afb3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 May 2024 12:20:01 +0200
+Subject: gpio: tqmx86: store IRQ trigger type and unmask status separately
+
+From: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
+
+[ 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 <matthias.schiffer@ew.tq-group.com>
+Link: https://lore.kernel.org/r/6aa4f207f77cb58ef64ffb947e91949b0f753ccd.1717063994.git.matthias.schiffer@ew.tq-group.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9de3955b30f638a11671c90bef872414ae790407 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 10 Jun 2024 15:57:18 -0700
+Subject: gve: ignore nonrelevant GSO type bits when processing TSO headers
+
+From: Joshua Washington <joshwash@google.com>
+
+[ 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 <joshwash@google.com>
+Reviewed-by: Praveen Kaligineedi <pkaligineedi@google.com>
+Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Suggested-by: Eric Dumazet <edumazet@google.com>
+Acked-by: Andrei Vagin <avagin@gmail.com>
+
+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 <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 03c6b01636a2864dbba970c6ac8562a5aed4f23d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 May 2024 07:19:14 -0700
+Subject: HID: core: remove unnecessary WARN_ON() in implement()
+
+From: Nikita Zhandarovich <n.zhandarovich@fintech.ru>
+
+[ 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:
+ <TASK>
+ __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: <syzbot+5186630949e3c55f0799@syzkaller.appspotmail.com>
+Suggested-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Nikita Zhandarovich <n.zhandarovich@fintech.ru>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a6519a5637f6e36fdcf0df44458a0785197ea84a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jose.exposito89@gmail.com>
+
+[ 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 <jose.exposito89@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b49915176559697088a0eb68874c1b15af6123d1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 May 2024 08:42:20 +0800
+Subject: iommu/amd: Fix sysfs leak in iommu init
+
+From: Kun(llfl) <llfl@linux.alibaba.com>
+
+[ 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) <llfl@linux.alibaba.com>
+Reviewed-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
+Link: https://lore.kernel.org/r/c8e0d11c6ab1ee48299c288009cf9c5dae07b42d.1715215003.git.llfl@linux.alibaba.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 258c19e3342f514e0ca2ae130591a36d7a045ab7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 12 Jun 2024 06:04:46 +0000
+Subject: ionic: fix use after netif_napi_del()
+
+From: Taehee Yoo <ap420073@gmail.com>
+
+[ 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 <interface name> rx 1 tx 1 combined 0
+ ethtool -L <interface name> rx 0 tx 0 combined 1
+ ethtool -L <interface name> 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:
+ <TASK>
+ ? 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 <ap420073@gmail.com>
+Reviewed-by: Brett Creeley <brett.creeley@amd.com>
+Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
+Link: https://lore.kernel.org/r/20240612060446.1754392-1-ap420073@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e1e7a9367469b8a81d501fbf5e1673886ccbca3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <amishin@t-argos.ru>
+
+[ 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 <amishin@t-argos.ru>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0e590181b2aa50c2e5523452f49b07b3bdeb4d84 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <razor@blackwall.org>
+
+[ 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 <razor@blackwall.org>
+Link: https://lore.kernel.org/r/20240609103654.914987-3-razor@blackwall.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2766cf15f14460c9c78328a5f1b8e9b9afb01daa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <razor@blackwall.org>
+
+[ 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 <razor@blackwall.org>
+Link: https://lore.kernel.org/r/20240609103654.914987-2-razor@blackwall.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6d0b7b0fc40778fc5cc5bf466f0547a8fe558078 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Jun 2024 15:20:58 +0800
+Subject: net: hns3: add cond_resched() to hns3 ring buffer init process
+
+From: Jie Wang <wangjie125@huawei.com>
+
+[ 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 <wangjie125@huawei.com>
+Signed-off-by: Jijie Shao <shaojijie@huawei.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bc374fae8d27614ba806f4a57fe418af03b061f6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Jun 2024 15:20:57 +0800
+Subject: net: hns3: fix kernel crash problem in concurrent scenario
+
+From: Yonglong Liu <liuyonglong@huawei.com>
+
+[ 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 <liuyonglong@huawei.com>
+Signed-off-by: Jijie Shao <shaojijie@huawei.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From cdc964e3344b88619b8925ccec0c6ac4392af4d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <petr.pavlu@suse.com>
+
+[ 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 <petr.pavlu@suse.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Link: https://lore.kernel.org/r/20240607112828.30285-1-petr.pavlu@suse.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dc27cb0512dbd53bd3b0817f11aea9b90d85ad6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gal@nvidia.com>
+
+[ 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 <gal@nvidia.com>
+Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1f1840123cb99fabd1d9ce0c603060c6c5bcf2fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 10 Jun 2024 10:34:26 +0200
+Subject: net: pse-pd: Use EOPNOTSUPP error code instead of ENOTSUPP
+
+From: Kory Maincent <kory.maincent@bootlin.com>
+
+[ 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 <andrew@lunn.ch>
+Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
+Link: https://lore.kernel.org/r/20240610083426.740660-1-kory.maincent@bootlin.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 692aa3117997dadc26c17c13e85f468eba35d95b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <csokas.bence@prolan.hu>
+
+[ 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" <csokas.bence@prolan.hu>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240605084251.63502-1-csokas.bence@prolan.hu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7a65ddcadcdf6d42013551fa67a7729a400487df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xiaolei.wang@windriver.com>
+
+[ 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 <xiaolei.wang@windriver.com>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240608143524.2065736-1-xiaolei.wang@windriver.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 174e4426067de245b35d03a3b6aa5ccbb6f180da Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kadlec@netfilter.org>
+
+[ 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 <nnamrec@gmail.com>
+Tested-by: Lion Ackermann <nnamrec@gmail.com>
+Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e363822882ca60f0ded2432ba6f5feca284f5d0a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Jun 2024 12:23:31 +0200
+Subject: netfilter: Use flowlabel flow key when re-routing mangled packets
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <yiche@redhat.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 16a2702bbee17d76af374fd55d2262be68736275 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 May 2024 13:27:17 +1000
+Subject: NFS: add barriers when testing for NFS_FSDATA_BLOCKED
+
+From: NeilBrown <neilb@suse.de>
+
+[ 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 <richard+debian+bugreport@kojedz.in>
+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 <neilb@suse.de>
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bc1152b4565235786cc41edbc645f08a1eff041f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 May 2024 15:44:35 -0400
+Subject: NFSv4.1 enforce rootpath check in fs_location query
+
+From: Olga Kornievskaia <kolga@netapp.com>
+
+[ 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 <kolga@netapp.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e92b7364cb1b56796b20917d3dc4879e400488e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 12 Jun 2024 16:02:40 +0200
+Subject: nvmet-passthru: propagate status from id override functions
+
+From: Daniel Wagner <dwagner@suse.de>
+
+[ 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 <dwagner@suse.de>
+Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eaf2a14459ca5aa8e1523355a1756e9b41951ea0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <W_Armin@gmx.de>
+
+[ 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 <W_Armin@gmx.de>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20240528204903.445546-1-W_Armin@gmx.de
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/container_of.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/capability.h>
+@@ -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
+
--- /dev/null
+From 07c1d5f64ab8eea0c646c22d76f762af3f0035cf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 May 2024 09:00:22 +0900
+Subject: selftests/ftrace: Fix to check required event file
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ 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) <mhiramat@kernel.org>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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[:[<group>/][<event>]] <attached-group>.<attached-event> [<args>]":README
++# requires: dynamic_events "e[:[<group>/][<event>]] <attached-group>.<attached-event> [<args>]":README events/syscalls/sys_enter_openat
+
+ echo 0 > events/enable
+
+--
+2.43.0
+
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
--- /dev/null
+From 30238f3eaf011530329a66e7ff0a2e941d8092ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 May 2024 16:47:16 +0800
+Subject: SUNRPC: return proper error from gss_wrap_req_priv
+
+From: Chen Hanxiao <chenhx.fnst@fujitsu.com>
+
+[ Upstream commit 33c94d7e3cb84f6d130678d6d59ba475a6c489cf ]
+
+don't return 0 if snd_buf->len really greater than snd_buf->buflen
+
+Signed-off-by: Chen Hanxiao <chenhx.fnst@fujitsu.com>
+Fixes: 0c77668ddb4e ("SUNRPC: Introduce trace points in rpc_auth_gss.ko")
+Reviewed-by: Benjamin Coddington <bcodding@redhat.com>
+Reviewed-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 99b75c373db7c89f26c26e3ccdfc87670cad2e7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Jun 2024 15:46:51 +0000
+Subject: tcp: fix race in tcp_v6_syn_recv_sock()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ 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 <edumazet@google.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+