--- /dev/null
+From 4847334176513a6ac636ad17bc6e3c48e2f568ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 10 Jul 2024 14:32:25 -0700
+Subject: lsm: infrastructure management of the sock security
+
+From: Casey Schaufler <casey@schaufler-ca.com>
+
+[ Upstream commit 2aff9d20d50ac45dd13a013ef5231f4fb8912356 ]
+
+Move management of the sock->sk_security blob out
+of the individual security modules and into the security
+infrastructure. Instead of allocating the blobs from within
+the modules the modules tell the infrastructure how much
+space is required, and the space is allocated there.
+
+Acked-by: Paul Moore <paul@paul-moore.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Reviewed-by: John Johansen <john.johansen@canonical.com>
+Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
+[PM: subject tweak]
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Stable-dep-of: 63dff3e48871 ("lsm: add the inode_free_security_rcu() LSM implementation hook")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/lsm_hooks.h | 1 +
+ security/apparmor/include/net.h | 3 +-
+ security/apparmor/lsm.c | 17 +------
+ security/apparmor/net.c | 2 +-
+ security/security.c | 36 +++++++++++++-
+ security/selinux/hooks.c | 80 ++++++++++++++-----------------
+ security/selinux/include/objsec.h | 5 ++
+ security/selinux/netlabel.c | 23 ++++-----
+ security/smack/smack.h | 5 ++
+ security/smack/smack_lsm.c | 70 +++++++++++++--------------
+ security/smack/smack_netfilter.c | 4 +-
+ 11 files changed, 133 insertions(+), 113 deletions(-)
+
+diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
+index a2ade0ffe9e7d..efd4a0655159c 100644
+--- a/include/linux/lsm_hooks.h
++++ b/include/linux/lsm_hooks.h
+@@ -73,6 +73,7 @@ struct lsm_blob_sizes {
+ int lbs_cred;
+ int lbs_file;
+ int lbs_inode;
++ int lbs_sock;
+ int lbs_superblock;
+ int lbs_ipc;
+ int lbs_msg_msg;
+diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
+index 67bf888c3bd6b..c42ed8a73f1ce 100644
+--- a/security/apparmor/include/net.h
++++ b/security/apparmor/include/net.h
+@@ -51,10 +51,9 @@ struct aa_sk_ctx {
+ struct aa_label *peer;
+ };
+
+-#define SK_CTX(X) ((X)->sk_security)
+ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
+ {
+- return sk->sk_security;
++ return sk->sk_security + apparmor_blob_sizes.lbs_sock;
+ }
+
+ #define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
+diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
+index 808060f9effb7..f5d05297d59ee 100644
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -1058,27 +1058,12 @@ static int apparmor_userns_create(const struct cred *cred)
+ return error;
+ }
+
+-static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
+-{
+- struct aa_sk_ctx *ctx;
+-
+- ctx = kzalloc(sizeof(*ctx), flags);
+- if (!ctx)
+- return -ENOMEM;
+-
+- sk->sk_security = ctx;
+-
+- return 0;
+-}
+-
+ static void apparmor_sk_free_security(struct sock *sk)
+ {
+ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+- sk->sk_security = NULL;
+ aa_put_label(ctx->label);
+ aa_put_label(ctx->peer);
+- kfree(ctx);
+ }
+
+ /**
+@@ -1433,6 +1418,7 @@ struct lsm_blob_sizes apparmor_blob_sizes __ro_after_init = {
+ .lbs_cred = sizeof(struct aa_label *),
+ .lbs_file = sizeof(struct aa_file_ctx),
+ .lbs_task = sizeof(struct aa_task_ctx),
++ .lbs_sock = sizeof(struct aa_sk_ctx),
+ };
+
+ static const struct lsm_id apparmor_lsmid = {
+@@ -1478,7 +1464,6 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
+ LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
+
+- LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
+ LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
+
+diff --git a/security/apparmor/net.c b/security/apparmor/net.c
+index 87e934b2b5488..77413a5191179 100644
+--- a/security/apparmor/net.c
++++ b/security/apparmor/net.c
+@@ -151,7 +151,7 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
+ const char *op, u32 request,
+ struct sock *sk)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+ int error = 0;
+
+ AA_BUG(!label);
+diff --git a/security/security.c b/security/security.c
+index 41ab07eafc7fa..43166e341526c 100644
+--- a/security/security.c
++++ b/security/security.c
+@@ -29,6 +29,7 @@
+ #include <linux/msg.h>
+ #include <linux/overflow.h>
+ #include <net/flow.h>
++#include <net/sock.h>
+
+ /* How many LSMs were built into the kernel? */
+ #define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+@@ -227,6 +228,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+ lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
+ lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
+ lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
++ lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
+ lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
+ lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+ lsm_set_blob_size(&needed->lbs_xattr_count,
+@@ -401,6 +403,7 @@ static void __init ordered_lsm_init(void)
+ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
+ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
+ init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
++ init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
+ init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
+ init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+ init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
+@@ -4673,6 +4676,28 @@ int security_socket_getpeersec_dgram(struct socket *sock,
+ }
+ EXPORT_SYMBOL(security_socket_getpeersec_dgram);
+
++/**
++ * lsm_sock_alloc - allocate a composite sock blob
++ * @sock: the sock that needs a blob
++ * @priority: allocation mode
++ *
++ * Allocate the sock blob for all the modules
++ *
++ * Returns 0, or -ENOMEM if memory can't be allocated.
++ */
++static int lsm_sock_alloc(struct sock *sock, gfp_t priority)
++{
++ if (blob_sizes.lbs_sock == 0) {
++ sock->sk_security = NULL;
++ return 0;
++ }
++
++ sock->sk_security = kzalloc(blob_sizes.lbs_sock, priority);
++ if (sock->sk_security == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
+ /**
+ * security_sk_alloc() - Allocate and initialize a sock's LSM blob
+ * @sk: sock
+@@ -4686,7 +4711,14 @@ EXPORT_SYMBOL(security_socket_getpeersec_dgram);
+ */
+ int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+ {
+- return call_int_hook(sk_alloc_security, sk, family, priority);
++ int rc = lsm_sock_alloc(sk, priority);
++
++ if (unlikely(rc))
++ return rc;
++ rc = call_int_hook(sk_alloc_security, sk, family, priority);
++ if (unlikely(rc))
++ security_sk_free(sk);
++ return rc;
+ }
+
+ /**
+@@ -4698,6 +4730,8 @@ int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+ void security_sk_free(struct sock *sk)
+ {
+ call_void_hook(sk_free_security, sk);
++ kfree(sk->sk_security);
++ sk->sk_security = NULL;
+ }
+
+ /**
+diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
+index 400eca4ad0fb6..c11303d662d80 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -4594,7 +4594,7 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
+
+ static int sock_has_perm(struct sock *sk, u32 perms)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+
+@@ -4662,7 +4662,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
+ isec->initialized = LABEL_INITIALIZED;
+
+ if (sock->sk) {
+- sksec = sock->sk->sk_security;
++ sksec = selinux_sock(sock->sk);
+ sksec->sclass = sclass;
+ sksec->sid = sid;
+ /* Allows detection of the first association on this socket */
+@@ -4678,8 +4678,8 @@ static int selinux_socket_post_create(struct socket *sock, int family,
+ static int selinux_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+ {
+- struct sk_security_struct *sksec_a = socka->sk->sk_security;
+- struct sk_security_struct *sksec_b = sockb->sk->sk_security;
++ struct sk_security_struct *sksec_a = selinux_sock(socka->sk);
++ struct sk_security_struct *sksec_b = selinux_sock(sockb->sk);
+
+ sksec_a->peer_sid = sksec_b->sid;
+ sksec_b->peer_sid = sksec_a->sid;
+@@ -4694,7 +4694,7 @@ static int selinux_socket_socketpair(struct socket *socka,
+ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+ {
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 family;
+ int err;
+
+@@ -4834,7 +4834,7 @@ static int selinux_socket_connect_helper(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+ {
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ int err;
+
+ err = sock_has_perm(sk, SOCKET__CONNECT);
+@@ -5012,9 +5012,9 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
+ struct sock *other,
+ struct sock *newsk)
+ {
+- struct sk_security_struct *sksec_sock = sock->sk_security;
+- struct sk_security_struct *sksec_other = other->sk_security;
+- struct sk_security_struct *sksec_new = newsk->sk_security;
++ struct sk_security_struct *sksec_sock = selinux_sock(sock);
++ struct sk_security_struct *sksec_other = selinux_sock(other);
++ struct sk_security_struct *sksec_new = selinux_sock(newsk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+ int err;
+@@ -5043,8 +5043,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
+ static int selinux_socket_unix_may_send(struct socket *sock,
+ struct socket *other)
+ {
+- struct sk_security_struct *ssec = sock->sk->sk_security;
+- struct sk_security_struct *osec = other->sk->sk_security;
++ struct sk_security_struct *ssec = selinux_sock(sock->sk);
++ struct sk_security_struct *osec = selinux_sock(other->sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+
+@@ -5081,7 +5081,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+ u16 family)
+ {
+ int err = 0;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u32 sk_sid = sksec->sid;
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+@@ -5110,7 +5110,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+ int err, peerlbl_active, secmark_active;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 family = sk->sk_family;
+ u32 sk_sid = sksec->sid;
+ struct common_audit_data ad;
+@@ -5178,7 +5178,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock,
+ int err = 0;
+ char *scontext = NULL;
+ u32 scontext_len;
+- struct sk_security_struct *sksec = sock->sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sock->sk);
+ u32 peer_sid = SECSID_NULL;
+
+ if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
+@@ -5238,34 +5238,27 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock,
+
+ static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+ {
+- struct sk_security_struct *sksec;
+-
+- sksec = kzalloc(sizeof(*sksec), priority);
+- if (!sksec)
+- return -ENOMEM;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ sksec->peer_sid = SECINITSID_UNLABELED;
+ sksec->sid = SECINITSID_UNLABELED;
+ sksec->sclass = SECCLASS_SOCKET;
+ selinux_netlbl_sk_security_reset(sksec);
+- sk->sk_security = sksec;
+
+ return 0;
+ }
+
+ static void selinux_sk_free_security(struct sock *sk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+- sk->sk_security = NULL;
+ selinux_netlbl_sk_security_free(sksec);
+- kfree(sksec);
+ }
+
+ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->sid = sksec->sid;
+ newsksec->peer_sid = sksec->peer_sid;
+@@ -5279,7 +5272,7 @@ static void selinux_sk_getsecid(const struct sock *sk, u32 *secid)
+ if (!sk)
+ *secid = SECINITSID_ANY_SOCKET;
+ else {
+- const struct sk_security_struct *sksec = sk->sk_security;
++ const struct sk_security_struct *sksec = selinux_sock(sk);
+
+ *secid = sksec->sid;
+ }
+@@ -5289,7 +5282,7 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
+ {
+ struct inode_security_struct *isec =
+ inode_security_novalidate(SOCK_INODE(parent));
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
+ sk->sk_family == PF_UNIX)
+@@ -5306,7 +5299,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
+ {
+ struct sock *sk = asoc->base.sk;
+ u16 family = sk->sk_family;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+ int err;
+@@ -5361,7 +5354,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
+ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
+ struct sk_buff *skb)
+ {
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+ u32 conn_sid;
+ int err;
+
+@@ -5394,7 +5387,7 @@ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
+ static int selinux_sctp_assoc_established(struct sctp_association *asoc,
+ struct sk_buff *skb)
+ {
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+
+ if (!selinux_policycap_extsockclass())
+ return 0;
+@@ -5493,8 +5486,8 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
+ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk,
+ struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ /* If policy does not support SECCLASS_SCTP_SOCKET then call
+ * the non-sctp clone version.
+@@ -5510,8 +5503,8 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
+
+ static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
+ {
+- struct sk_security_struct *ssksec = ssk->sk_security;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *ssksec = selinux_sock(ssk);
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ ssksec->sclass = sksec->sclass;
+ ssksec->sid = sksec->sid;
+@@ -5526,7 +5519,7 @@ static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
+ static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ int err;
+ u16 family = req->rsk_ops->family;
+ u32 connsid;
+@@ -5547,7 +5540,7 @@ static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ static void selinux_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+ {
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->sid = req->secid;
+ newsksec->peer_sid = req->peer_secid;
+@@ -5564,7 +5557,7 @@ static void selinux_inet_csk_clone(struct sock *newsk,
+ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
+ {
+ u16 family = sk->sk_family;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+@@ -5639,7 +5632,7 @@ static int selinux_tun_dev_attach_queue(void *security)
+ static int selinux_tun_dev_attach(struct sock *sk, void *security)
+ {
+ struct tun_security_struct *tunsec = security;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ /* we don't currently perform any NetLabel based labeling here and it
+ * isn't clear that we would want to do so anyway; while we could apply
+@@ -5762,7 +5755,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
+ return NF_ACCEPT;
+
+ /* standard practice, label using the parent socket */
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+ sid = sksec->sid;
+ } else
+ sid = SECINITSID_KERNEL;
+@@ -5785,7 +5778,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
+ sk = skb_to_full_sk(skb);
+ if (sk == NULL)
+ return NF_ACCEPT;
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+
+ ad_net_init_from_iif(&ad, &net, state->out->ifindex, state->pf);
+ if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
+@@ -5874,7 +5867,7 @@ static unsigned int selinux_ip_postroute(void *priv,
+ u32 skb_sid;
+ struct sk_security_struct *sksec;
+
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+ if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
+ return NF_DROP;
+ /* At this point, if the returned skb peerlbl is SECSID_NULL
+@@ -5903,7 +5896,7 @@ static unsigned int selinux_ip_postroute(void *priv,
+ } else {
+ /* Locally generated packet, fetch the security label from the
+ * associated socket. */
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ peer_sid = sksec->sid;
+ secmark_perm = PACKET__SEND;
+ }
+@@ -5946,7 +5939,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+ unsigned int data_len = skb->len;
+ unsigned char *data = skb->data;
+ struct nlmsghdr *nlh;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 sclass = sksec->sclass;
+ u32 perm;
+
+@@ -7004,6 +6997,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct inode_security_struct),
+ .lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_msg_msg = sizeof(struct msg_security_struct),
++ .lbs_sock = sizeof(struct sk_security_struct),
+ .lbs_superblock = sizeof(struct superblock_security_struct),
+ .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
+ };
+diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
+index dea1d6f3ed2d3..b074099acbaf7 100644
+--- a/security/selinux/include/objsec.h
++++ b/security/selinux/include/objsec.h
+@@ -195,4 +195,9 @@ selinux_superblock(const struct super_block *superblock)
+ return superblock->s_security + selinux_blob_sizes.lbs_superblock;
+ }
+
++static inline struct sk_security_struct *selinux_sock(const struct sock *sock)
++{
++ return sock->sk_security + selinux_blob_sizes.lbs_sock;
++}
++
+ #endif /* _SELINUX_OBJSEC_H_ */
+diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
+index 55885634e8804..fbe5f8c29f813 100644
+--- a/security/selinux/netlabel.c
++++ b/security/selinux/netlabel.c
+@@ -17,6 +17,7 @@
+ #include <linux/gfp.h>
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
++#include <linux/lsm_hooks.h>
+ #include <net/sock.h>
+ #include <net/netlabel.h>
+ #include <net/ip.h>
+@@ -68,7 +69,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
+ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_secattr != NULL)
+@@ -100,7 +101,7 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
+ const struct sock *sk,
+ u32 sid)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
+
+ if (secattr == NULL)
+@@ -240,7 +241,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ * being labeled by it's parent socket, if it is just exit */
+ sk = skb_to_full_sk(skb);
+ if (sk != NULL) {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sksec->nlbl_state != NLBL_REQSKB)
+ return 0;
+@@ -277,7 +278,7 @@ int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc,
+ {
+ int rc;
+ struct netlbl_lsm_secattr secattr;
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+
+@@ -356,7 +357,7 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
+ */
+ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (family == PF_INET)
+ sksec->nlbl_state = NLBL_LABELED;
+@@ -374,8 +375,8 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
+ */
+ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->nlbl_state = sksec->nlbl_state;
+ }
+@@ -393,7 +394,7 @@ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
+ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ if (family != PF_INET && family != PF_INET6)
+@@ -510,7 +511,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ {
+ int rc = 0;
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr secattr;
+
+ if (selinux_netlbl_option(level, optname) &&
+@@ -548,7 +549,7 @@ static int selinux_netlbl_socket_connect_helper(struct sock *sk,
+ struct sockaddr *addr)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ /* connected sockets are allowed to disconnect when the address family
+@@ -587,7 +588,7 @@ static int selinux_netlbl_socket_connect_helper(struct sock *sk,
+ int selinux_netlbl_socket_connect_locked(struct sock *sk,
+ struct sockaddr *addr)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+diff --git a/security/smack/smack.h b/security/smack/smack.h
+index 041688e5a77a3..297f21446f456 100644
+--- a/security/smack/smack.h
++++ b/security/smack/smack.h
+@@ -355,6 +355,11 @@ static inline struct superblock_smack *smack_superblock(
+ return superblock->s_security + smack_blob_sizes.lbs_superblock;
+ }
+
++static inline struct socket_smack *smack_sock(const struct sock *sock)
++{
++ return sock->sk_security + smack_blob_sizes.lbs_sock;
++}
++
+ /*
+ * Is the directory transmuting?
+ */
+diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
+index 002a1b9ed83a5..6ec9a40f3ec59 100644
+--- a/security/smack/smack_lsm.c
++++ b/security/smack/smack_lsm.c
+@@ -1606,7 +1606,7 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ isp = ssp->smk_in;
+@@ -1994,7 +1994,7 @@ static int smack_file_receive(struct file *file)
+
+ if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
+ sock = SOCKET_I(inode);
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ tsp = smack_cred(current_cred());
+ /*
+ * If the receiving process can't write to the
+@@ -2409,11 +2409,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
+ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+ {
+ struct smack_known *skp = smk_of_current();
+- struct socket_smack *ssp;
+-
+- ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
+- if (ssp == NULL)
+- return -ENOMEM;
++ struct socket_smack *ssp = smack_sock(sk);
+
+ /*
+ * Sockets created by kernel threads receive web label.
+@@ -2427,11 +2423,10 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+ }
+ ssp->smk_packet = NULL;
+
+- sk->sk_security = ssp;
+-
+ return 0;
+ }
+
++#ifdef SMACK_IPV6_PORT_LABELING
+ /**
+ * smack_sk_free_security - Free a socket blob
+ * @sk: the socket
+@@ -2440,7 +2435,6 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+ */
+ static void smack_sk_free_security(struct sock *sk)
+ {
+-#ifdef SMACK_IPV6_PORT_LABELING
+ struct smk_port_label *spp;
+
+ if (sk->sk_family == PF_INET6) {
+@@ -2453,9 +2447,8 @@ static void smack_sk_free_security(struct sock *sk)
+ }
+ rcu_read_unlock();
+ }
+-#endif
+- kfree(sk->sk_security);
+ }
++#endif
+
+ /**
+ * smack_sk_clone_security - Copy security context
+@@ -2466,8 +2459,8 @@ static void smack_sk_free_security(struct sock *sk)
+ */
+ static void smack_sk_clone_security(const struct sock *sk, struct sock *newsk)
+ {
+- struct socket_smack *ssp_old = sk->sk_security;
+- struct socket_smack *ssp_new = newsk->sk_security;
++ struct socket_smack *ssp_old = smack_sock(sk);
++ struct socket_smack *ssp_new = smack_sock(newsk);
+
+ *ssp_new = *ssp_old;
+ }
+@@ -2583,7 +2576,7 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
+ */
+ static int smack_netlbl_add(struct sock *sk)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = ssp->smk_out;
+ int rc;
+
+@@ -2616,7 +2609,7 @@ static int smack_netlbl_add(struct sock *sk)
+ */
+ static void smack_netlbl_delete(struct sock *sk)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+
+ /*
+ * Take the label off the socket if one is set.
+@@ -2648,7 +2641,7 @@ static int smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
+ struct smack_known *skp;
+ int rc = 0;
+ struct smack_known *hkp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smk_audit_info ad;
+
+ rcu_read_lock();
+@@ -2721,7 +2714,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
+ {
+ struct sock *sk = sock->sk;
+ struct sockaddr_in6 *addr6;
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+ struct smk_port_label *spp;
+ unsigned short port = 0;
+
+@@ -2809,7 +2802,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
+ int act)
+ {
+ struct smk_port_label *spp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = NULL;
+ unsigned short port;
+ struct smack_known *object;
+@@ -2912,7 +2905,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ ssp->smk_in = skp;
+@@ -2960,7 +2953,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
+ * Sockets created by kernel threads receive web label.
+ */
+ if (unlikely(current->flags & PF_KTHREAD)) {
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ ssp->smk_in = &smack_known_web;
+ ssp->smk_out = &smack_known_web;
+ }
+@@ -2985,8 +2978,8 @@ static int smack_socket_post_create(struct socket *sock, int family,
+ static int smack_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+ {
+- struct socket_smack *asp = socka->sk->sk_security;
+- struct socket_smack *bsp = sockb->sk->sk_security;
++ struct socket_smack *asp = smack_sock(socka->sk);
++ struct socket_smack *bsp = smack_sock(sockb->sk);
+
+ asp->smk_packet = bsp->smk_out;
+ bsp->smk_packet = asp->smk_out;
+@@ -3049,7 +3042,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
+ if (__is_defined(SMACK_IPV6_SECMARK_LABELING))
+ rsp = smack_ipv6host_label(sip);
+ if (rsp != NULL) {
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
+ SMK_CONNECTING);
+@@ -3844,9 +3837,9 @@ static int smack_unix_stream_connect(struct sock *sock,
+ {
+ struct smack_known *skp;
+ struct smack_known *okp;
+- struct socket_smack *ssp = sock->sk_security;
+- struct socket_smack *osp = other->sk_security;
+- struct socket_smack *nsp = newsk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock);
++ struct socket_smack *osp = smack_sock(other);
++ struct socket_smack *nsp = smack_sock(newsk);
+ struct smk_audit_info ad;
+ int rc = 0;
+ #ifdef CONFIG_AUDIT
+@@ -3898,8 +3891,8 @@ static int smack_unix_stream_connect(struct sock *sock,
+ */
+ static int smack_unix_may_send(struct socket *sock, struct socket *other)
+ {
+- struct socket_smack *ssp = sock->sk->sk_security;
+- struct socket_smack *osp = other->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
++ struct socket_smack *osp = smack_sock(other->sk);
+ struct smk_audit_info ad;
+ int rc;
+
+@@ -3936,7 +3929,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+ struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
+ #endif
+ #ifdef SMACK_IPV6_SECMARK_LABELING
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+ struct smack_known *rsp;
+ #endif
+ int rc = 0;
+@@ -4148,7 +4141,7 @@ static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
+ netlbl_secattr_init(&secattr);
+
+ if (sk)
+- ssp = sk->sk_security;
++ ssp = smack_sock(sk);
+
+ if (netlbl_skbuff_getattr(skb, family, &secattr) == 0) {
+ skp = smack_from_secattr(&secattr, ssp);
+@@ -4170,7 +4163,7 @@ static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
+ */
+ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = NULL;
+ int rc = 0;
+ struct smk_audit_info ad;
+@@ -4274,7 +4267,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
+ u32 slen = 1;
+ int rc = 0;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ if (ssp->smk_packet != NULL) {
+ rcp = ssp->smk_packet->smk_known;
+ slen = strlen(rcp) + 1;
+@@ -4324,7 +4317,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
+
+ switch (family) {
+ case PF_UNIX:
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ s = ssp->smk_out->smk_secid;
+ break;
+ case PF_INET:
+@@ -4373,7 +4366,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
+ (sk->sk_family != PF_INET && sk->sk_family != PF_INET6))
+ return;
+
+- ssp = sk->sk_security;
++ ssp = smack_sock(sk);
+ ssp->smk_in = skp;
+ ssp->smk_out = skp;
+ /* cssp->smk_packet is already set in smack_inet_csk_clone() */
+@@ -4393,7 +4386,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ {
+ u16 family = sk->sk_family;
+ struct smack_known *skp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct sockaddr_in addr;
+ struct iphdr *hdr;
+ struct smack_known *hskp;
+@@ -4479,7 +4472,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ static void smack_inet_csk_clone(struct sock *sk,
+ const struct request_sock *req)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp;
+
+ if (req->peer_secid != 0) {
+@@ -5049,6 +5042,7 @@ struct lsm_blob_sizes smack_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct inode_smack),
+ .lbs_ipc = sizeof(struct smack_known *),
+ .lbs_msg_msg = sizeof(struct smack_known *),
++ .lbs_sock = sizeof(struct socket_smack),
+ .lbs_superblock = sizeof(struct superblock_smack),
+ .lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
+ };
+@@ -5173,7 +5167,9 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(socket_getpeersec_stream, smack_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security),
++#ifdef SMACK_IPV6_PORT_LABELING
+ LSM_HOOK_INIT(sk_free_security, smack_sk_free_security),
++#endif
+ LSM_HOOK_INIT(sk_clone_security, smack_sk_clone_security),
+ LSM_HOOK_INIT(sock_graft, smack_sock_graft),
+ LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request),
+diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
+index b945c1d3a7431..bad71b7e648da 100644
+--- a/security/smack/smack_netfilter.c
++++ b/security/smack/smack_netfilter.c
+@@ -26,8 +26,8 @@ static unsigned int smack_ip_output(void *priv,
+ struct socket_smack *ssp;
+ struct smack_known *skp;
+
+- if (sk && sk->sk_security) {
+- ssp = sk->sk_security;
++ if (sk) {
++ ssp = smack_sock(sk);
+ skp = ssp->smk_out;
+ skb->secmark = skp->smk_secid;
+ }
+--
+2.43.0
+
--- /dev/null
+From f138bd8383484d807836ba2e13d7a8960e792729 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Sep 2024 15:13:32 +0200
+Subject: serial: qcom-geni: fix arg types for qcom_geni_serial_poll_bit()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit c2eaf5e01275ae13f1ec5b1434f6c49cfff57430 ]
+
+The "offset" passed in should be unsigned since it's always a positive
+offset from our memory mapped IO.
+
+The "field" should be u32 since we're anding it with a 32-bit value
+read from the device.
+
+Suggested-by: Stephen Boyd <swboyd@chromium.org>
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
+Link: https://lore.kernel.org/r/20240610152420.v4.4.I24a0de52dd7336908df180fa6b698e001f3aff82@changeid
+Tested-by: NÃcolas F. R. A. Prado <nfraprado@collabora.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20240906131336.23625-5-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: cc4a0e5754a1 ("serial: qcom-geni: fix console corruption")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/qcom_geni_serial.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
+index b88435c0ea507..54052c68555d7 100644
+--- a/drivers/tty/serial/qcom_geni_serial.c
++++ b/drivers/tty/serial/qcom_geni_serial.c
+@@ -266,7 +266,7 @@ static bool qcom_geni_serial_secondary_active(struct uart_port *uport)
+ }
+
+ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+- int offset, int field, bool set)
++ unsigned int offset, u32 field, bool set)
+ {
+ u32 reg;
+ struct qcom_geni_serial_port *port;
+--
+2.43.0
+
--- /dev/null
+From 71284b18956d1f1c65074aa90c3fac15227bf583 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Sep 2024 15:13:34 +0200
+Subject: serial: qcom-geni: fix console corruption
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Johan Hovold <johan+linaro@kernel.org>
+
+[ Upstream commit cc4a0e5754a16bbc1e215c091349a7c83a2c5e14 ]
+
+The Qualcomm serial console implementation is broken and can lose
+characters when the serial port is also used for tty output.
+
+Specifically, the console code only waits for the current tx command to
+complete when all data has already been written to the fifo. When there
+are on-going longer transfers this often means that console output is
+lost when the console code inadvertently "hijacks" the current tx
+command instead of starting a new one.
+
+This can, for example, be observed during boot when console output that
+should have been interspersed with init output is truncated:
+
+ [ 9.462317] qcom-snps-eusb2-hsphy fde000.phy: Registered Qcom-eUSB2 phy
+ [ OK ] Found device KBG50ZNS256G KIOXIA Wi[ 9.471743ndows.
+ [ 9.539915] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
+
+Add a new state variable to track how much data has been written to the
+fifo and use it to determine when the fifo and shift register are both
+empty. This is needed since there is currently no other known way to
+determine when the shift register is empty.
+
+This in turn allows the console code to interrupt long transfers without
+losing data.
+
+Note that the oops-in-progress case is similarly broken as it does not
+cancel any active command and also waits for the wrong status flag when
+attempting to drain the fifo (TX_FIFO_NOT_EMPTY_EN is only set when
+cancelling a command leaves data in the fifo).
+
+Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP")
+Fixes: a1fee899e5be ("tty: serial: qcom_geni_serial: Fix softlock")
+Fixes: 9e957a155005 ("serial: qcom-geni: Don't cancel/abort if we can't get the port lock")
+Cc: stable@vger.kernel.org # 4.17
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Tested-by: NÃcolas F. R. A. Prado <nfraprado@collabora.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20240906131336.23625-7-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/qcom_geni_serial.c | 45 +++++++++++++--------------
+ 1 file changed, 22 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
+index 7bbd70c306201..f8f6e9466b400 100644
+--- a/drivers/tty/serial/qcom_geni_serial.c
++++ b/drivers/tty/serial/qcom_geni_serial.c
+@@ -131,6 +131,7 @@ struct qcom_geni_serial_port {
+ bool brk;
+
+ unsigned int tx_remaining;
++ unsigned int tx_queued;
+ int wakeup_irq;
+ bool rx_tx_swap;
+ bool cts_rts_swap;
+@@ -144,6 +145,8 @@ static const struct uart_ops qcom_geni_uart_pops;
+ static struct uart_driver qcom_geni_console_driver;
+ static struct uart_driver qcom_geni_uart_driver;
+
++static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
++
+ static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
+ {
+ return container_of(uport, struct qcom_geni_serial_port, uport);
+@@ -393,6 +396,14 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
+ #endif
+
+ #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
++static void qcom_geni_serial_drain_fifo(struct uart_port *uport)
++{
++ struct qcom_geni_serial_port *port = to_dev_port(uport);
++
++ qcom_geni_serial_poll_bitfield(uport, SE_GENI_M_GP_LENGTH, GP_LENGTH,
++ port->tx_queued);
++}
++
+ static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch)
+ {
+ struct qcom_geni_private_data *private_data = uport->private_data;
+@@ -468,7 +479,6 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
+ struct qcom_geni_serial_port *port;
+ bool locked = true;
+ unsigned long flags;
+- u32 geni_status;
+
+ WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
+
+@@ -482,34 +492,20 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
+ else
+ uart_port_lock_irqsave(uport, &flags);
+
+- geni_status = readl(uport->membase + SE_GENI_STATUS);
++ if (qcom_geni_serial_main_active(uport)) {
++ /* Wait for completion or drain FIFO */
++ if (!locked || port->tx_remaining == 0)
++ qcom_geni_serial_poll_tx_done(uport);
++ else
++ qcom_geni_serial_drain_fifo(uport);
+
+- if (!locked) {
+- /*
+- * We can only get here if an oops is in progress then we were
+- * unable to get the lock. This means we can't safely access
+- * our state variables like tx_remaining. About the best we
+- * can do is wait for the FIFO to be empty before we start our
+- * transfer, so we'll do that.
+- */
+- qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+- M_TX_FIFO_NOT_EMPTY_EN, false);
+- } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) {
+- /*
+- * It seems we can't interrupt existing transfers if all data
+- * has been sent, in which case we need to look for done first.
+- */
+- qcom_geni_serial_poll_tx_done(uport);
++ qcom_geni_serial_cancel_tx_cmd(uport);
+ }
+
+ __qcom_geni_serial_console_write(uport, s, count);
+
+-
+- if (locked) {
+- if (port->tx_remaining)
+- qcom_geni_serial_setup_tx(uport, port->tx_remaining);
++ if (locked)
+ uart_port_unlock_irqrestore(uport, flags);
+- }
+ }
+
+ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+@@ -690,6 +686,7 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
+ writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+
+ port->tx_remaining = 0;
++ port->tx_queued = 0;
+ }
+
+ static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
+@@ -916,6 +913,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
+ if (!port->tx_remaining) {
+ qcom_geni_serial_setup_tx(uport, pending);
+ port->tx_remaining = pending;
++ port->tx_queued = 0;
+
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
+@@ -924,6 +922,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
+ }
+
+ qcom_geni_serial_send_chunk_fifo(uport, chunk);
++ port->tx_queued += chunk;
+
+ /*
+ * The tx fifo watermark is level triggered and latched. Though we had
+--
+2.43.0
+
--- /dev/null
+From 0827839650381c5b2b36673f556944af8bf27c70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Sep 2024 15:13:33 +0200
+Subject: serial: qcom-geni: introduce qcom_geni_serial_poll_bitfield()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit b26d1ad1221273c88c2c4f5b4080338b8ca23859 ]
+
+With a small modification the qcom_geni_serial_poll_bit() function
+could be used to poll more than just a single bit. Let's generalize
+it. We'll make the qcom_geni_serial_poll_bit() into just a wrapper of
+the general function.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
+Link: https://lore.kernel.org/r/20240610152420.v4.5.Ic6411eab8d9d37acc451705f583fb535cd6dadb2@changeid
+Tested-by: NÃcolas F. R. A. Prado <nfraprado@collabora.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20240906131336.23625-6-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: cc4a0e5754a1 ("serial: qcom-geni: fix console corruption")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/qcom_geni_serial.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
+index 54052c68555d7..7bbd70c306201 100644
+--- a/drivers/tty/serial/qcom_geni_serial.c
++++ b/drivers/tty/serial/qcom_geni_serial.c
+@@ -265,8 +265,8 @@ static bool qcom_geni_serial_secondary_active(struct uart_port *uport)
+ return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE;
+ }
+
+-static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+- unsigned int offset, u32 field, bool set)
++static bool qcom_geni_serial_poll_bitfield(struct uart_port *uport,
++ unsigned int offset, u32 field, u32 val)
+ {
+ u32 reg;
+ struct qcom_geni_serial_port *port;
+@@ -286,7 +286,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+ timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10;
+ while (timeout_us) {
+ reg = readl(uport->membase + offset);
+- if ((bool)(reg & field) == set)
++ if ((reg & field) == val)
+ return true;
+ udelay(10);
+ timeout_us -= 10;
+@@ -294,6 +294,12 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+ return false;
+ }
+
++static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
++ unsigned int offset, u32 field, bool set)
++{
++ return qcom_geni_serial_poll_bitfield(uport, offset, field, set ? field : 0);
++}
++
+ static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
+ {
+ u32 m_cmd;
+--
+2.43.0
+
arm-dts-imx6ul-geam-fix-fsl-pins-property-in-tscgrp-pinctrl.patch
arm-dts-imx6ull-seeed-npi-fix-fsl-pins-property-in-tscgrp-pinctrl.patch
tools-nolibc-include-arch.h-from-string.h.patch
+soc-versatile-realview-fix-memory-leak-during-device.patch
+soc-versatile-realview-fix-soc_dev-leak-during-devic.patch
+usb-typec-ucsi-call-cancel-from-single-location.patch
+usb-typec-ucsi-fix-busy-loop-on-asus-vivobooks.patch
+soc-qcom-geni-se-add-gp_length-irq_en_set-irq_en_cle.patch
+serial-qcom-geni-fix-arg-types-for-qcom_geni_serial_.patch
+serial-qcom-geni-introduce-qcom_geni_serial_poll_bit.patch
+serial-qcom-geni-fix-console-corruption.patch
+thermal-core-store-trip-sysfs-attributes-in-thermal_.patch
+thermal-sysfs-get-to-trips-via-attribute-pointers.patch
+thermal-sysfs-refine-the-handling-of-trip-hysteresis.patch
+thermal-sysfs-add-sanity-checks-for-trip-temperature.patch
+lsm-infrastructure-management-of-the-sock-security.patch
--- /dev/null
+From fbe43be15e67d88a19e55a23e5c7a4db23951953 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 6 Sep 2024 15:13:31 +0200
+Subject: soc: qcom: geni-se: add GP_LENGTH/IRQ_EN_SET/IRQ_EN_CLEAR registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit b03ffc76b83c1a7d058454efbcf1bf0e345ef1c2 ]
+
+For UART devices the M_GP_LENGTH is the TX word count. For other
+devices this is the transaction word count.
+
+For UART devices the S_GP_LENGTH is the RX word count.
+
+The IRQ_EN set/clear registers allow you to set or clear bits in the
+IRQ_EN register without needing a read-modify-write.
+
+Acked-by: Bjorn Andersson <andersson@kernel.org>
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Link: https://lore.kernel.org/r/20240610152420.v4.1.Ife7ced506aef1be3158712aa3ff34a006b973559@changeid
+Tested-by: NÃcolas F. R. A. Prado <nfraprado@collabora.com>
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Link: https://lore.kernel.org/r/20240906131336.23625-4-johan+linaro@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: cc4a0e5754a1 ("serial: qcom-geni: fix console corruption")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/soc/qcom/geni-se.h | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
+index 0f038a1a03309..c3bca9c0bf2cf 100644
+--- a/include/linux/soc/qcom/geni-se.h
++++ b/include/linux/soc/qcom/geni-se.h
+@@ -88,11 +88,15 @@ struct geni_se {
+ #define SE_GENI_M_IRQ_STATUS 0x610
+ #define SE_GENI_M_IRQ_EN 0x614
+ #define SE_GENI_M_IRQ_CLEAR 0x618
++#define SE_GENI_M_IRQ_EN_SET 0x61c
++#define SE_GENI_M_IRQ_EN_CLEAR 0x620
+ #define SE_GENI_S_CMD0 0x630
+ #define SE_GENI_S_CMD_CTRL_REG 0x634
+ #define SE_GENI_S_IRQ_STATUS 0x640
+ #define SE_GENI_S_IRQ_EN 0x644
+ #define SE_GENI_S_IRQ_CLEAR 0x648
++#define SE_GENI_S_IRQ_EN_SET 0x64c
++#define SE_GENI_S_IRQ_EN_CLEAR 0x650
+ #define SE_GENI_TX_FIFOn 0x700
+ #define SE_GENI_RX_FIFOn 0x780
+ #define SE_GENI_TX_FIFO_STATUS 0x800
+@@ -101,6 +105,8 @@ struct geni_se {
+ #define SE_GENI_RX_WATERMARK_REG 0x810
+ #define SE_GENI_RX_RFR_WATERMARK_REG 0x814
+ #define SE_GENI_IOS 0x908
++#define SE_GENI_M_GP_LENGTH 0x910
++#define SE_GENI_S_GP_LENGTH 0x914
+ #define SE_DMA_TX_IRQ_STAT 0xc40
+ #define SE_DMA_TX_IRQ_CLR 0xc44
+ #define SE_DMA_TX_FSM_RST 0xc58
+@@ -234,6 +240,9 @@ struct geni_se {
+ #define IO2_DATA_IN BIT(1)
+ #define RX_DATA_IN BIT(0)
+
++/* SE_GENI_M_GP_LENGTH and SE_GENI_S_GP_LENGTH fields */
++#define GP_LENGTH GENMASK(31, 0)
++
+ /* SE_DMA_TX_IRQ_STAT Register fields */
+ #define TX_DMA_DONE BIT(0)
+ #define TX_EOT BIT(1)
+--
+2.43.0
+
--- /dev/null
+From 07d5b7e267b5c57be2a75f62b3ac1a85a709dc8e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 25 Aug 2024 20:05:23 +0200
+Subject: soc: versatile: realview: fix memory leak during device remove
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+[ Upstream commit 1c4f26a41f9d052f334f6ae629e01f598ed93508 ]
+
+If device is unbound, the memory allocated for soc_dev_attr should be
+freed to prevent leaks.
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/20240825-soc-dev-fixes-v1-2-ff4b35abed83@linaro.org
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Stable-dep-of: c774f2564c00 ("soc: versatile: realview: fix soc_dev leak during device remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/soc/versatile/soc-realview.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c
+index c6876d232d8fd..d304ee69287af 100644
+--- a/drivers/soc/versatile/soc-realview.c
++++ b/drivers/soc/versatile/soc-realview.c
+@@ -93,7 +93,7 @@ static int realview_soc_probe(struct platform_device *pdev)
+ if (IS_ERR(syscon_regmap))
+ return PTR_ERR(syscon_regmap);
+
+- soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
++ soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+@@ -106,10 +106,9 @@ static int realview_soc_probe(struct platform_device *pdev)
+ soc_dev_attr->family = "Versatile";
+ soc_dev_attr->custom_attr_group = realview_groups[0];
+ soc_dev = soc_device_register(soc_dev_attr);
+- if (IS_ERR(soc_dev)) {
+- kfree(soc_dev_attr);
++ if (IS_ERR(soc_dev))
+ return -ENODEV;
+- }
++
+ ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET,
+ &realview_coreid);
+ if (ret)
+--
+2.43.0
+
--- /dev/null
+From 386cb0f19b79d3c4f9b95effb883e409b28c8902 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 25 Aug 2024 20:05:24 +0200
+Subject: soc: versatile: realview: fix soc_dev leak during device remove
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+[ Upstream commit c774f2564c0086c23f5269fd4691f233756bf075 ]
+
+If device is unbound, the soc_dev should be unregistered to prevent
+memory leak.
+
+Fixes: a2974c9c1f83 ("soc: add driver for the ARM RealView")
+Cc: stable@vger.kernel.org
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/20240825-soc-dev-fixes-v1-3-ff4b35abed83@linaro.org
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/soc/versatile/soc-realview.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c
+index d304ee69287af..cf91abe07d38d 100644
+--- a/drivers/soc/versatile/soc-realview.c
++++ b/drivers/soc/versatile/soc-realview.c
+@@ -4,6 +4,7 @@
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
++#include <linux/device.h>
+ #include <linux/init.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+@@ -81,6 +82,13 @@ static struct attribute *realview_attrs[] = {
+
+ ATTRIBUTE_GROUPS(realview);
+
++static void realview_soc_socdev_release(void *data)
++{
++ struct soc_device *soc_dev = data;
++
++ soc_device_unregister(soc_dev);
++}
++
+ static int realview_soc_probe(struct platform_device *pdev)
+ {
+ struct regmap *syscon_regmap;
+@@ -109,6 +117,11 @@ static int realview_soc_probe(struct platform_device *pdev)
+ if (IS_ERR(soc_dev))
+ return -ENODEV;
+
++ ret = devm_add_action_or_reset(&pdev->dev, realview_soc_socdev_release,
++ soc_dev);
++ if (ret)
++ return ret;
++
+ ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET,
+ &realview_coreid);
+ if (ret)
+--
+2.43.0
+
--- /dev/null
+From 20a223c4112b4b837451a103f6214b7f828e1536 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jul 2024 18:25:03 +0200
+Subject: thermal: core: Store trip sysfs attributes in thermal_trip_desc
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 66b263306a86c0f2d3cdb44e3722db6cff3a32b3 ]
+
+Instead of allocating memory for trip point sysfs attributes separately,
+store them in struct thermal_trip_desc for each trip individually which
+allows a few extra memory allocations to be avoided.
+
+No intentional functional impact.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Link: https://patch.msgid.link/46571375.fMDQidcC6G@rjwysocki.net
+Stable-dep-of: 874b6476fa88 ("thermal: sysfs: Add sanity checks for trip temperature and hysteresis")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thermal/thermal_core.h | 23 +++----
+ drivers/thermal/thermal_sysfs.c | 105 +++++++++++---------------------
+ 2 files changed, 46 insertions(+), 82 deletions(-)
+
+diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
+index 4cf2b7230d04b..5be8bef41b926 100644
+--- a/drivers/thermal/thermal_core.h
++++ b/drivers/thermal/thermal_core.h
+@@ -15,8 +15,20 @@
+ #include "thermal_netlink.h"
+ #include "thermal_debugfs.h"
+
++struct thermal_attr {
++ struct device_attribute attr;
++ char name[THERMAL_NAME_LENGTH];
++};
++
++struct thermal_trip_attrs {
++ struct thermal_attr type;
++ struct thermal_attr temp;
++ struct thermal_attr hyst;
++};
++
+ struct thermal_trip_desc {
+ struct thermal_trip trip;
++ struct thermal_trip_attrs trip_attrs;
+ struct list_head notify_list_node;
+ int notify_temp;
+ int threshold;
+@@ -56,9 +68,6 @@ struct thermal_governor {
+ * @device: &struct device for this thermal zone
+ * @removal: removal completion
+ * @resume: resume completion
+- * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
+- * @trip_type_attrs: attributes for trip points for sysfs: trip type
+- * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
+ * @mode: current mode of this thermal zone
+ * @devdata: private pointer for device private data
+ * @num_trips: number of trip points the thermal zone supports
+@@ -102,9 +111,6 @@ struct thermal_zone_device {
+ struct completion removal;
+ struct completion resume;
+ struct attribute_group trips_attribute_group;
+- struct thermal_attr *trip_temp_attrs;
+- struct thermal_attr *trip_type_attrs;
+- struct thermal_attr *trip_hyst_attrs;
+ enum thermal_device_mode mode;
+ void *devdata;
+ int num_trips;
+@@ -188,11 +194,6 @@ int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
+
+ struct thermal_zone_device *thermal_zone_get_by_id(int id);
+
+-struct thermal_attr {
+- struct device_attribute attr;
+- char name[THERMAL_NAME_LENGTH];
+-};
+-
+ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
+ {
+ return cdev->ops->get_requested_power && cdev->ops->state2power &&
+diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
+index 72b302bf914e3..f12d5d47da9bd 100644
+--- a/drivers/thermal/thermal_sysfs.c
++++ b/drivers/thermal/thermal_sysfs.c
+@@ -382,87 +382,55 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = {
+ */
+ static int create_trip_attrs(struct thermal_zone_device *tz)
+ {
+- const struct thermal_trip_desc *td;
++ struct thermal_trip_desc *td;
+ struct attribute **attrs;
+-
+- /* This function works only for zones with at least one trip */
+- if (tz->num_trips <= 0)
+- return -EINVAL;
+-
+- tz->trip_type_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_type_attrs),
+- GFP_KERNEL);
+- if (!tz->trip_type_attrs)
+- return -ENOMEM;
+-
+- tz->trip_temp_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_temp_attrs),
+- GFP_KERNEL);
+- if (!tz->trip_temp_attrs) {
+- kfree(tz->trip_type_attrs);
+- return -ENOMEM;
+- }
+-
+- tz->trip_hyst_attrs = kcalloc(tz->num_trips,
+- sizeof(*tz->trip_hyst_attrs),
+- GFP_KERNEL);
+- if (!tz->trip_hyst_attrs) {
+- kfree(tz->trip_type_attrs);
+- kfree(tz->trip_temp_attrs);
+- return -ENOMEM;
+- }
++ int i;
+
+ attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
+- if (!attrs) {
+- kfree(tz->trip_type_attrs);
+- kfree(tz->trip_temp_attrs);
+- kfree(tz->trip_hyst_attrs);
++ if (!attrs)
+ return -ENOMEM;
+- }
+
++ i = 0;
+ for_each_trip_desc(tz, td) {
+- int indx = thermal_zone_trip_id(tz, &td->trip);
++ struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;
+
+ /* create trip type attribute */
+- snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
+- "trip_point_%d_type", indx);
++ snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH,
++ "trip_point_%d_type", i);
+
+- sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr);
+- tz->trip_type_attrs[indx].attr.attr.name =
+- tz->trip_type_attrs[indx].name;
+- tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO;
+- tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
+- attrs[indx] = &tz->trip_type_attrs[indx].attr.attr;
++ sysfs_attr_init(&trip_attrs->type.attr.attr);
++ trip_attrs->type.attr.attr.name = trip_attrs->type.name;
++ trip_attrs->type.attr.attr.mode = S_IRUGO;
++ trip_attrs->type.attr.show = trip_point_type_show;
++ attrs[i] = &trip_attrs->type.attr.attr;
+
+ /* create trip temp attribute */
+- snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
+- "trip_point_%d_temp", indx);
+-
+- sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr);
+- tz->trip_temp_attrs[indx].attr.attr.name =
+- tz->trip_temp_attrs[indx].name;
+- tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
+- tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
++ snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH,
++ "trip_point_%d_temp", i);
++
++ sysfs_attr_init(&trip_attrs->temp.attr.attr);
++ trip_attrs->temp.attr.attr.name = trip_attrs->temp.name;
++ trip_attrs->temp.attr.attr.mode = S_IRUGO;
++ trip_attrs->temp.attr.show = trip_point_temp_show;
+ if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) {
+- tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
+- tz->trip_temp_attrs[indx].attr.store =
+- trip_point_temp_store;
++ trip_attrs->temp.attr.attr.mode |= S_IWUSR;
++ trip_attrs->temp.attr.store = trip_point_temp_store;
+ }
+- attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr;
++ attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr;
+
+- snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH,
+- "trip_point_%d_hyst", indx);
++ snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH,
++ "trip_point_%d_hyst", i);
+
+- sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr);
+- tz->trip_hyst_attrs[indx].attr.attr.name =
+- tz->trip_hyst_attrs[indx].name;
+- tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
+- tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show;
++ sysfs_attr_init(&trip_attrs->hyst.attr.attr);
++ trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name;
++ trip_attrs->hyst.attr.attr.mode = S_IRUGO;
++ trip_attrs->hyst.attr.show = trip_point_hyst_show;
+ if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) {
+- tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR;
+- tz->trip_hyst_attrs[indx].attr.store =
+- trip_point_hyst_store;
++ trip_attrs->hyst.attr.attr.mode |= S_IWUSR;
++ trip_attrs->hyst.attr.store = trip_point_hyst_store;
+ }
+- attrs[indx + tz->num_trips * 2] =
+- &tz->trip_hyst_attrs[indx].attr.attr;
++ attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr;
++ i++;
+ }
+ attrs[tz->num_trips * 3] = NULL;
+
+@@ -479,13 +447,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
+ */
+ static void destroy_trip_attrs(struct thermal_zone_device *tz)
+ {
+- if (!tz)
+- return;
+-
+- kfree(tz->trip_type_attrs);
+- kfree(tz->trip_temp_attrs);
+- kfree(tz->trip_hyst_attrs);
+- kfree(tz->trips_attribute_group.attrs);
++ if (tz)
++ kfree(tz->trips_attribute_group.attrs);
+ }
+
+ int thermal_zone_create_device_groups(struct thermal_zone_device *tz)
+--
+2.43.0
+
--- /dev/null
+From 872ec8f8250f0f8f01157681c72b813f987f4009 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 26 Aug 2024 18:21:59 +0200
+Subject: thermal: sysfs: Add sanity checks for trip temperature and hysteresis
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 874b6476fa888e79e41daf7653384c8d70927b90 ]
+
+Add sanity checks for new trip temperature and hysteresis values to
+trip_point_temp_store() and trip_point_hyst_store() to prevent trip
+point threshold from falling below THERMAL_TEMP_INVALID.
+
+However, still allow user space to pass THERMAL_TEMP_INVALID as the
+new trip temperature value to invalidate the trip if necessary.
+
+Also allow the hysteresis to be updated when the temperature is invalid
+to allow user space to avoid having to adjust hysteresis after a valid
+temperature has been set, but in that case just change the value and do
+nothing else.
+
+Fixes: be0a3600aa1e ("thermal: sysfs: Rework the handling of trip point updates")
+Cc: 6.8+ <stable@vger.kernel.org> # 6.8+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
+Link: https://patch.msgid.link/12528772.O9o76ZdvQC@rjwysocki.net
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thermal/thermal_sysfs.c | 50 +++++++++++++++++++++++++--------
+ 1 file changed, 38 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
+index 2709455486776..d628dd67be5cc 100644
+--- a/drivers/thermal/thermal_sysfs.c
++++ b/drivers/thermal/thermal_sysfs.c
+@@ -111,18 +111,26 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
+
+ mutex_lock(&tz->lock);
+
+- if (temp != trip->temperature) {
+- if (tz->ops.set_trip_temp) {
+- ret = tz->ops.set_trip_temp(tz, trip, temp);
+- if (ret)
+- goto unlock;
+- }
++ if (temp == trip->temperature)
++ goto unlock;
+
+- thermal_zone_set_trip_temp(tz, trip, temp);
++ /* Arrange the condition to avoid integer overflows. */
++ if (temp != THERMAL_TEMP_INVALID &&
++ temp <= trip->hysteresis + THERMAL_TEMP_INVALID) {
++ ret = -EINVAL;
++ goto unlock;
++ }
+
+- __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
++ if (tz->ops.set_trip_temp) {
++ ret = tz->ops.set_trip_temp(tz, trip, temp);
++ if (ret)
++ goto unlock;
+ }
+
++ thermal_zone_set_trip_temp(tz, trip, temp);
++
++ __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
++
+ unlock:
+ mutex_unlock(&tz->lock);
+
+@@ -152,15 +160,33 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
+
+ mutex_lock(&tz->lock);
+
+- if (hyst != trip->hysteresis) {
+- thermal_zone_set_trip_hyst(tz, trip, hyst);
++ if (hyst == trip->hysteresis)
++ goto unlock;
++
++ /*
++ * Allow the hysteresis to be updated when the temperature is invalid
++ * to allow user space to avoid having to adjust hysteresis after a
++ * valid temperature has been set, but in that case just change the
++ * value and do nothing else.
++ */
++ if (trip->temperature == THERMAL_TEMP_INVALID) {
++ WRITE_ONCE(trip->hysteresis, hyst);
++ goto unlock;
++ }
+
+- __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
++ if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) {
++ ret = -EINVAL;
++ goto unlock;
+ }
+
++ thermal_zone_set_trip_hyst(tz, trip, hyst);
++
++ __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
++
++unlock:
+ mutex_unlock(&tz->lock);
+
+- return count;
++ return ret ? ret : count;
+ }
+
+ static ssize_t
+--
+2.43.0
+
--- /dev/null
+From 4d6569d10c6f8d6d634fd8a2b2dc2af1ce9191dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jul 2024 18:25:59 +0200
+Subject: thermal: sysfs: Get to trips via attribute pointers
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit afd84fb10ced3caf53769ba734ea237bde0f69e3 ]
+
+The _store() and _show() functions for sysfs attributes corresponding
+to trip point parameters (type, temperature and hysteresis) read the
+trip ID from the attribute name and then use the trip ID as the index
+in the given thermal zone's trips table to get to the trip object they
+want.
+
+Instead of doing this, make them use the attribute pointer they get
+as the second argument to get to the trip object embedded in the same
+struct thermal_trip_desc as the struct device_attribute pointed to by
+it, which is much more straightforward and less overhead.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Link: https://patch.msgid.link/114841552.nniJfEyVGO@rjwysocki.net
+Stable-dep-of: 874b6476fa88 ("thermal: sysfs: Add sanity checks for trip temperature and hysteresis")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thermal/thermal_sysfs.c | 54 ++++++++++++---------------------
+ 1 file changed, 20 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
+index f12d5d47da9bd..11a34b9fe153d 100644
+--- a/drivers/thermal/thermal_sysfs.c
++++ b/drivers/thermal/thermal_sysfs.c
+@@ -12,6 +12,7 @@
+
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
++#include <linux/container_of.h>
+ #include <linux/sysfs.h>
+ #include <linux/device.h>
+ #include <linux/err.h>
+@@ -78,39 +79,38 @@ mode_store(struct device *dev, struct device_attribute *attr,
+ return count;
+ }
+
++#define thermal_trip_of_attr(_ptr_, _attr_) \
++ ({ \
++ struct thermal_trip_desc *td; \
++ \
++ td = container_of(_ptr_, struct thermal_trip_desc, \
++ trip_attrs._attr_.attr); \
++ &td->trip; \
++ })
++
+ static ssize_t
+ trip_point_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+- struct thermal_zone_device *tz = to_thermal_zone(dev);
+- int trip_id;
+-
+- if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
+- return -EINVAL;
++ struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
+
+- return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type));
++ return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
+ }
+
+ static ssize_t
+ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+ {
++ struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+- struct thermal_trip *trip;
+- int trip_id, ret;
+- int temp;
++ int ret, temp;
+
+ ret = kstrtoint(buf, 10, &temp);
+ if (ret)
+ return -EINVAL;
+
+- if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
+- return -EINVAL;
+-
+ mutex_lock(&tz->lock);
+
+- trip = &tz->trips[trip_id].trip;
+-
+ if (temp != trip->temperature) {
+ if (tz->ops.set_trip_temp) {
+ ret = tz->ops.set_trip_temp(tz, trip, temp);
+@@ -133,35 +133,25 @@ static ssize_t
+ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+- struct thermal_zone_device *tz = to_thermal_zone(dev);
+- int trip_id;
++ struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
+
+- if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
+- return -EINVAL;
+-
+- return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.temperature));
++ return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
+ }
+
+ static ssize_t
+ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+ {
++ struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+- struct thermal_trip *trip;
+- int trip_id, ret;
+- int hyst;
++ int ret, hyst;
+
+ ret = kstrtoint(buf, 10, &hyst);
+ if (ret || hyst < 0)
+ return -EINVAL;
+
+- if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
+- return -EINVAL;
+-
+ mutex_lock(&tz->lock);
+
+- trip = &tz->trips[trip_id].trip;
+-
+ if (hyst != trip->hysteresis) {
+ WRITE_ONCE(trip->hysteresis, hyst);
+
+@@ -177,13 +167,9 @@ static ssize_t
+ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+- struct thermal_zone_device *tz = to_thermal_zone(dev);
+- int trip_id;
+-
+- if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
+- return -EINVAL;
++ struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
+
+- return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.hysteresis));
++ return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis));
+ }
+
+ static ssize_t
+--
+2.43.0
+
--- /dev/null
+From 2f9e752579ecf6f424af7bf7ca656b4458084cfc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Jul 2024 18:27:25 +0200
+Subject: thermal: sysfs: Refine the handling of trip hysteresis changes
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 107280e1371f1ba183be1ac88e91ec60cad33c18 ]
+
+Change trip_point_hyst_store() and replace thermal_zone_trip_updated()
+with thermal_zone_set_trip_hyst() to follow the trip_point_temp_store()
+code pattern for more consistency.
+
+No intentional functional impact.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Link: https://patch.msgid.link/5508466.Sb9uPGUboI@rjwysocki.net
+Stable-dep-of: 874b6476fa88 ("thermal: sysfs: Add sanity checks for trip temperature and hysteresis")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thermal/thermal_core.h | 4 ++--
+ drivers/thermal/thermal_sysfs.c | 4 ++--
+ drivers/thermal/thermal_trip.c | 6 +++---
+ 3 files changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
+index 5be8bef41b926..3c8e2bca87f2d 100644
+--- a/drivers/thermal/thermal_core.h
++++ b/drivers/thermal/thermal_core.h
+@@ -263,11 +263,11 @@ const char *thermal_trip_type_name(enum thermal_trip_type trip_type);
+ void thermal_zone_set_trips(struct thermal_zone_device *tz);
+ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
+ const struct thermal_trip *trip);
+-void thermal_zone_trip_updated(struct thermal_zone_device *tz,
+- const struct thermal_trip *trip);
+ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
+ void thermal_zone_trip_down(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip);
++void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
++ struct thermal_trip *trip, int hyst);
+
+ /* sysfs I/F */
+ int thermal_zone_create_device_groups(struct thermal_zone_device *tz);
+diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
+index 11a34b9fe153d..2709455486776 100644
+--- a/drivers/thermal/thermal_sysfs.c
++++ b/drivers/thermal/thermal_sysfs.c
+@@ -153,9 +153,9 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
+ mutex_lock(&tz->lock);
+
+ if (hyst != trip->hysteresis) {
+- WRITE_ONCE(trip->hysteresis, hyst);
++ thermal_zone_set_trip_hyst(tz, trip, hyst);
+
+- thermal_zone_trip_updated(tz, trip);
++ __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
+ }
+
+ mutex_unlock(&tz->lock);
+diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c
+index 06a0554ddc389..fa5ac9b512312 100644
+--- a/drivers/thermal/thermal_trip.c
++++ b/drivers/thermal/thermal_trip.c
+@@ -138,11 +138,11 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
+ return trip_to_trip_desc(trip) - tz->trips;
+ }
+
+-void thermal_zone_trip_updated(struct thermal_zone_device *tz,
+- const struct thermal_trip *trip)
++void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
++ struct thermal_trip *trip, int hyst)
+ {
++ WRITE_ONCE(trip->hysteresis, hyst);
+ thermal_notify_tz_trip_change(tz, trip);
+- __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
+ }
+
+ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
+--
+2.43.0
+
--- /dev/null
+From 0da7831d0033b0d96195c9ea26b6b8feea0dd65a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 16 Aug 2024 16:58:58 +0300
+Subject: usb: typec: ucsi: Call CANCEL from single location
+
+From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+
+[ Upstream commit 4f322657ade1b784bb2b1ba8761469ae87520681 ]
+
+The command cancellation can be done right after detecting
+that the PPM is busy. There is no need to do it separately
+in ucsi_read_error() and ucsi_send_command_common().
+
+Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://lore.kernel.org/r/20240816135859.3499351-6-heikki.krogerus@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 7fa6b25dfb43 ("usb: typec: ucsi: Fix busy loop on ASUS VivoBooks")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c | 17 ++++-------------
+ 1 file changed, 4 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
+index 17155ed17fdf8..26a28c7b680b1 100644
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -115,7 +115,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
+ return ret;
+
+ if (*cci & UCSI_CCI_BUSY)
+- return -EBUSY;
++ return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
+
+ if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
+ return -EIO;
+@@ -148,15 +148,7 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
+ int ret;
+
+ command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
+- ret = ucsi_run_command(ucsi, command, &cci,
+- &error, sizeof(error), false);
+-
+- if (cci & UCSI_CCI_BUSY) {
+- ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);
+-
+- return ret ? ret : -EBUSY;
+- }
+-
++ ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
+ if (ret < 0)
+ return ret;
+
+@@ -238,9 +230,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
+ mutex_lock(&ucsi->ppm_lock);
+
+ ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
+- if (cci & UCSI_CCI_BUSY)
+- ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false) ?: -EBUSY;
+- else if (cci & UCSI_CCI_ERROR)
++
++ if (cci & UCSI_CCI_ERROR)
+ ret = ucsi_read_error(ucsi, connector_num);
+
+ mutex_unlock(&ucsi->ppm_lock);
+--
+2.43.0
+
--- /dev/null
+From 98db8c27a9d4377d2710270b36252171c05a3da3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 12 Sep 2024 09:41:32 +0200
+Subject: usb: typec: ucsi: Fix busy loop on ASUS VivoBooks
+
+From: Christian A. Ehrhardt <lk@c--e.de>
+
+[ Upstream commit 7fa6b25dfb43dafc0e16510e2fcfd63634fc95c2 ]
+
+If the busy indicator is set, all other fields in CCI should be
+clear according to the spec. However, some UCSI implementations do
+not follow this rule and report bogus data in CCI along with the
+busy indicator. Ignore the contents of CCI if the busy indicator is
+set.
+
+If a command timeout is hit it is possible that the EVENT_PENDING
+bit is cleared while connector work is still scheduled which can
+cause the EVENT_PENDING bit to go out of sync with scheduled connector
+work. Check and set the EVENT_PENDING bit on entry to
+ucsi_handle_connector_change() to fix this.
+
+Finally, check UCSI_CCI_BUSY before the return code of ->sync_control.
+This ensures that the command is cancelled even if ->sync_control
+returns an error (most likely -ETIMEDOUT).
+
+Reported-by: Anurag Bijea <icaliberdev@gmail.com>
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219108
+Bisected-by: Christian Heusel <christian@heusel.eu>
+Tested-by: Anurag Bijea <icaliberdev@gmail.com>
+Fixes: de52aca4d9d5 ("usb: typec: ucsi: Never send a lone connector change ack")
+Cc: stable@vger.kernel.org
+Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://lore.kernel.org/r/20240912074132.722855-1-lk@c--e.de
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
+index 26a28c7b680b1..8cc43c866130a 100644
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -38,6 +38,10 @@
+
+ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
+ {
++ /* Ignore bogus data in CCI if busy indicator is set. */
++ if (cci & UCSI_CCI_BUSY)
++ return;
++
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+
+@@ -107,15 +111,13 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
+ size = clamp(size, 0, 16);
+
+ ret = ucsi->ops->sync_control(ucsi, command);
+- if (ret)
+- return ret;
+-
+- ret = ucsi->ops->read_cci(ucsi, cci);
+- if (ret)
+- return ret;
++ if (ucsi->ops->read_cci(ucsi, cci))
++ return -EIO;
+
+ if (*cci & UCSI_CCI_BUSY)
+ return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
++ if (ret)
++ return ret;
+
+ if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
+ return -EIO;
+@@ -1240,6 +1242,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
+
+ mutex_lock(&con->lock);
+
++ if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
++ dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n",
++ __func__);
++
+ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
+
+ ret = ucsi_send_command_common(ucsi, command, &con->status,
+--
+2.43.0
+