6.12.111-rc1 review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 524/1102 smb/client: validate new EOF for insert range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 1519dc88c87f5346dae0464d7d6da1b6bf1f6e8e ]
smb3_insert_range() does not check if the new file size (i_size + len) is valid. This allows FALLOC_FL_INSERT_RANGE to bypass RLIMIT_FSIZE, exceed s_maxbytes, or produce a size outside the loff_t range.
Use check_add_overflow() to calculate the new EOF. Validate it with inode_newsize_ok() before modifying the file.
Reproducer, using a file on a CIFS mount:
bash -c '
FILE=/mnt/cifs/repro
trap "" SIGXFSZ
ulimit -f 3072 # RLIMIT_FSIZE = 3 MiB
# A regular write is stopped at 3 MiB.
dd if=/dev/zero of="$FILE" bs=1M count=4 status=none stat -c "size after write: %s" "$FILE"
# Insert 2 MiB into a 2 MiB file. truncate -s 2M "$FILE" fallocate -i -o 0 -l 2M "$FILE" stat -c "size after insert: %s" "$FILE"
'
Before this change, the regular write stops at the 3 MiB limit, but insert range grows the file to 4 MiB:
dd: error writing '/mnt/cifs/repro': File too large size after write: 3145728 size after insert: 4194304
After this change, insert range also fails at the limit and leaves the 2 MiB file unchanged:
dd: error writing '/mnt/cifs/repro': File too large size after write: 3145728 fallocate: fallocate failed: File too large size after insert: 2097152
--- fs/smb/client/smb2ops.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
fs/smb/client/smb2ops.c +10 -2
@@ -3765,7 +3765,8 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,struct cifsFileInfo *cfile = file->private_data;struct inode *inode =file_inode (file);struct cifsInodeInfo *cifsi =CIFS_I (inode); -__u64 count, old_eof, new_eof; +u64 count; +loff_t old_eof, new_eof; xid =get_xid ();
@@ -3775,8 +3776,15 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, goto out; } +if (check_add_overflow (old_eof, len, &new_eof)) { + rc = -EFBIG ; + goto out; + } + rc =inode_newsize_ok (inode, new_eof); +if (rc) + goto out; + count = old_eof - off; - new_eof = old_eof + len;filemap_invalidate_lock (inode->i_mapping); rc =filemap_write_and_wait_range (inode->i_mapping, off, new_eof -1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 525/1102 smb/client: validate new EOF for zero range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 88972e35750792e717af287dc71f42a03b5cbce4 ]
When FALLOC_FL_ZERO_RANGE is used without FALLOC_FL_KEEP_SIZE, smb3_zero_range() may extend EOF without checking RLIMIT_FSIZE, allowing the file to grow beyond the caller's file-size limit.
Fix this by calling inode_newsize_ok() before sending the zero-range request when the operation would extend EOF.
Reproducer, using a file on a CIFS mount:
bash -c ' FILE=/mnt/cifs/repro trap "" SIGXFSZ ulimit -f 3072 truncate -s 2M "$FILE" fallocate --zero-range -o 0 -l 4M "$FILE" echo "fallocate rc=$?" stat -c "file size=%s" "$FILE" '
Before this change, the operation succeeds despite the 3 MiB limit:
fallocate rc=0 file size=4194304
After this change, fallocate fails and leaves the file at 2 MiB.
--- fs/smb/client/smb2ops.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/smb/client/smb2ops.c +8 -1
@@ -3315,6 +3315,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,trace_smb3_zero_enter (xid, cfile->fid.persistent_fid, tcon->tid, ses->Suid, offset, len); + new_size = offset + len; +if (!keep_size &&i_size_read (inode) < new_size) { + rc =inode_newsize_ok (inode, new_size); +if (rc) + goto out; + } +filemap_invalidate_lock (inode->i_mapping); i_size =i_size_read (inode);
@@ -3346,7 +3353,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,/* * do we also need to change the size of the file? */ - new_size = offset + len;if (keep_size == false && (unsigned long long )i_size_read (inode) < new_size) { rc =SMB2_set_eof (xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_size);
@@ -3361,6 +3367,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, zero_range_exit:filemap_invalidate_unlock (inode->i_mapping); + out:free_xid (xid);if (rc) trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 526/1102 smb/client: mark file sparse before emulating insert range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit cd03ce4950d80147ac8f20bc03c42b75b0352407 ]
The SMB client emulates FALLOC_FL_INSERT_RANGE with SET_EOF, COPYCHUNK and SET_ZERO_DATA.
SET_ZERO_DATA creates a hole only when the file is sparse. On a non-sparse file, it clears the inserted range but leaves its blocks allocated, causing the extent count check in xfstests generic/064 to fail.
Fix this by marking the file sparse before modifying it.
This patch produces the expected sparse extents in xfstests generic/064 only when the server-reported block size is compatible with the server's deallocation granularity.
For ksmbd, the reported block size follows the backing filesystem, and the test passes. For Samba, the test passes with a block size matching the backend granularity, for example, 4 KiB on Btrfs, but not with the default 1 KiB value. For Windows Server 2022, 4 KiB inserts do not generate holes, while aligned inserts of 64 KiB or larger do.
--- fs/smb/client/smb2ops.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/smb/client/smb2ops.c +5 -0
@@ -3793,6 +3793,11 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, count = old_eof - off; +/* SET_ZERO_DATA creates a hole only in a sparse file. */ + rc =smb2_set_sparse (xid, tcon, cfile, inode, true); +if (rc) + goto out; +filemap_invalidate_lock (inode->i_mapping); rc =filemap_write_and_wait_range (inode->i_mapping, off, new_eof -1 );if (rc <0 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 527/1102 smb: client: batch SRV_COPYCHUNK entries to cut round trips
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
[ Upstream commit 68d2e2ca1cba9259e943bcd188671b619b9770b4 ]
smb2_copychunk_range() used to send a single SRV_COPYCHUNK per SRV_COPYCHUNK_COPY IOCTL.
Implement variable Chunks[] array in struct copychunk_ioctl and fill it with struct copychunk (MS-SMB2 2.2.31.1.1), bounded by server-advertised limits.
This reduces the number of IOCTL requests for large copies.
While we are at it, rename a couple variables to follow the terminology used in the specification.
Stable-dep-of: 0923ae9f23cc ("smb/client: fix data corruption in emulated insert range")
--- fs/smb/client/smb2ops.c | 306 +++++++++++++++++++++++++--------------- fs/smb/client/smb2pdu.h | 16 ++- fs/smb/client/trace.h | 2 +- 3 files changed, 207 insertions(+), 117 deletions(-)
fs/smb/client/smb2ops.c +196 -110
@@ -1832,140 +1832,226 @@ smb2_ioctl_query_info(const unsigned int xid,return rc; } +/** +* calc_chunk_count - calculates the number chunks to be filled in the Chunks[] +* array of struct copychunk_ioctl +* +* @tcon: destination file tcon +* @bytes_left: how many bytes are left to copy +* +* Return: maximum number of chunks with which Chunks[] can be filled. +*/ +static inline u32 +calc_chunk_count (struct cifs_tcon *tcon,u64 bytes_left) +{ +u32 max_chunks =READ_ONCE (tcon->max_chunks); +u32 max_bytes_copy =READ_ONCE (tcon->max_bytes_copy); +u32 max_bytes_chunk =READ_ONCE (tcon->max_bytes_chunk); +u64 need; +u32 allowed; + +if (!max_bytes_chunk || !max_bytes_copy || !max_chunks) +return 0 ; + +/* chunks needed for the remaining bytes */ + need =DIV_ROUND_UP_ULL (bytes_left, max_bytes_chunk); +/* chunks allowed per cc request */ + allowed =DIV_ROUND_UP (max_bytes_copy, max_bytes_chunk); + +return (u32 )umin (need,umin (max_chunks, allowed)); +} + +/** +* smb2_copychunk_range - server-side copy of data range +* +* @xid: transaction id +* @src_file: source file +* @dst_file: destination file +* @src_off: source file byte offset +* @len: number of bytes to copy +* @dst_off: destination file byte offset +* +* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE +* IOCTLs, splitting the request into chunks limited by tcon->max_*. +* +* Return: @len on success; negative errno on failure. +*/ static ssize_t smb2_copychunk_range (const unsigned int xid, -struct cifsFileInfo *srcfile, -struct cifsFileInfo *trgtfile,u64 src_off, -u64 len,u64 dest_off) +struct cifsFileInfo *src_file, +struct cifsFileInfo *dst_file, +u64 src_off, +u64 len, +u64 dst_off) { -int rc; -unsigned int ret_data_len; -struct copychunk_ioctl *pcchunk; -struct copychunk_ioctl_rsp *retbuf =NULL ; +int rc =0 ; +unsigned int ret_data_len =0 ; +struct copychunk_ioctl *cc_req =NULL ; +struct copychunk_ioctl_rsp *cc_rsp =NULL ;struct cifs_tcon *tcon; -int chunks_copied =0 ; -bool chunk_sizes_updated = false; -ssize_t bytes_written, total_bytes_written =0 ; +struct copychunk *chunk; +u32 chunks, chunk_count, chunk_bytes; +u32 copy_bytes, copy_bytes_left; +u32 chunks_written, bytes_written; +u64 total_bytes_left = len; +u64 src_off_prev, dst_off_prev; +u32 retries =0 ; + + tcon =tlink_tcon (dst_file->tlink); + +trace_smb3_copychunk_enter (xid, src_file->fid.volatile_fid, + dst_file->fid.volatile_fid, tcon->tid, + tcon->ses->Suid, src_off, dst_off, len); + +retry: + chunk_count =calc_chunk_count (tcon, total_bytes_left); +if (!chunk_count) { + rc = -EOPNOTSUPP ; + goto out; + } - pcchunk =kmalloc (sizeof (struct copychunk_ioctl ),GFP_KERNEL ); -if (pcchunk ==NULL ) -return -ENOMEM ; + cc_req =kzalloc (struct_size (cc_req, Chunks, chunk_count),GFP_KERNEL ); +if (!cc_req) { + rc = -ENOMEM ; + goto out; + } -cifs_dbg (FYI ,"%s: about to call request res key\n" , __func__);/* Request a key from the server to identify the source of the copy */ - rc =SMB2_request_res_key (xid,tlink_tcon (srcfile->tlink), - srcfile->fid.persistent_fid, - srcfile->fid.volatile_fid, pcchunk); + rc =SMB2_request_res_key (xid, +tlink_tcon (src_file->tlink), + src_file->fid.persistent_fid, + src_file->fid.volatile_fid, + cc_req); -/* Note: request_res_key sets res_key null only if rc !=0 */ +/* Note: request_res_key sets res_key null only if rc != 0 */ if (rc) - goto cchunk_out; + goto out; -/* For now array only one chunk long, will make more flexible later */ - pcchunk->ChunkCount =cpu_to_le32 (1 ); - pcchunk->Reserved =0 ; - pcchunk->Reserved2 =0 ; +while (total_bytes_left >0 ) { - tcon =tlink_tcon (trgtfile->tlink); +/* Store previous offsets to allow rewind */ + src_off_prev = src_off; + dst_off_prev = dst_off; -trace_smb3_copychunk_enter (xid, srcfile->fid.volatile_fid, - trgtfile->fid.volatile_fid, tcon->tid, - tcon->ses->Suid, src_off, dest_off, len); + chunks =0 ; + copy_bytes =0 ; + copy_bytes_left =umin (total_bytes_left, tcon->max_bytes_copy); +while (copy_bytes_left >0 && chunks < chunk_count) { + chunk = &cc_req->Chunks[chunks++]; -while (len >0 ) { - pcchunk->SourceOffset =cpu_to_le64 (src_off); - pcchunk->TargetOffset =cpu_to_le64 (dest_off); - pcchunk->Length = -cpu_to_le32 (min_t (u64, len, tcon->max_bytes_chunk)); + chunk->SourceOffset =cpu_to_le64 (src_off); + chunk->TargetOffset =cpu_to_le64 (dst_off); + + chunk_bytes =umin (copy_bytes_left, tcon->max_bytes_chunk); + + chunk->Length =cpu_to_le32 (chunk_bytes); +/* Buffer is zeroed, no need to set chunk->Reserved = 0 */ + + src_off += chunk_bytes; + dst_off += chunk_bytes; + + copy_bytes_left -= chunk_bytes; + copy_bytes += chunk_bytes; + } + + cc_req->ChunkCount =cpu_to_le32 (chunks); +/* Buffer is zeroed, no need to set cc_req->Reserved = 0 */ /* Request server copy to target from src identified by key */ -kfree (retbuf); - retbuf =NULL ; - rc =SMB2_ioctl (xid, tcon, trgtfile->fid.persistent_fid, - trgtfile->fid.volatile_fid,FSCTL_SRV_COPYCHUNK_WRITE , - (char *)pcchunk,sizeof (struct copychunk_ioctl ), - CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); +kfree (cc_rsp); + cc_rsp =NULL ; + rc =SMB2_ioctl (xid, tcon, dst_file->fid.persistent_fid, + dst_file->fid.volatile_fid,FSCTL_SRV_COPYCHUNK_WRITE , + (char *)cc_req,struct_size (cc_req, Chunks, chunks), + CIFSMaxBufSize, (char **)&cc_rsp, &ret_data_len); + +if (rc && rc != -EINVAL ) + goto out; + +if (unlikely (ret_data_len !=sizeof (*cc_rsp))) { +cifs_tcon_dbg (VFS ,"Copychunk invalid response: size %u/%zu\n" , + ret_data_len,sizeof (*cc_rsp)); + rc = -EIO ; + goto out; + } + + bytes_written =le32_to_cpu (cc_rsp->TotalBytesWritten); + chunks_written =le32_to_cpu (cc_rsp->ChunksWritten); + chunk_bytes =le32_to_cpu (cc_rsp->ChunkBytesWritten); +if (rc ==0 ) { -if (ret_data_len != -sizeof (struct copychunk_ioctl_rsp )) { -cifs_tcon_dbg (VFS ,"Invalid cchunk response size\n" ); - rc = -EIO ; - goto cchunk_out; - } -if (retbuf->TotalBytesWritten ==0 ) { -cifs_dbg (FYI ,"no bytes copied\n" ); - rc = -EIO ; - goto cchunk_out; - } -/* -* Check if server claimed to write more than we asked -*/ -if (le32_to_cpu (retbuf->TotalBytesWritten) > -le32_to_cpu (pcchunk->Length)) { -cifs_tcon_dbg (VFS ,"Invalid copy chunk response\n" ); +/* Check if server claimed to write more than we asked */ +if (unlikely (!bytes_written || bytes_written > copy_bytes || + !chunks_written || chunks_written > chunks)) { +cifs_tcon_dbg (VFS ,"Copychunk invalid response: bytes written %u/%u, chunks written %u/%u\n" , + bytes_written, copy_bytes, chunks_written, chunks); rc = -EIO ; - goto cchunk_out; + goto out; } -if (le32_to_cpu (retbuf->ChunksWritten) !=1 ) { -cifs_tcon_dbg (VFS ,"Invalid num chunks written\n" ); - rc = -EIO ; - goto cchunk_out; + +/* Partial write: rewind */ +if (bytes_written < copy_bytes) { +u32 delta = copy_bytes - bytes_written; + + src_off -= delta; + dst_off -= delta; } - chunks_copied++; - - bytes_written =le32_to_cpu (retbuf->TotalBytesWritten); - src_off += bytes_written; - dest_off += bytes_written; - len -= bytes_written; - total_bytes_written += bytes_written; - -cifs_dbg (FYI ,"Chunks %d PartialChunk %d Total %zu\n" , -le32_to_cpu (retbuf->ChunksWritten), -le32_to_cpu (retbuf->ChunkBytesWritten), - bytes_written); -trace_smb3_copychunk_done (xid, srcfile->fid.volatile_fid, - trgtfile->fid.volatile_fid, tcon->tid, - tcon->ses->Suid, src_off, dest_off, len); - }else if (rc == -EINVAL ) { -if (ret_data_len !=sizeof (struct copychunk_ioctl_rsp )) - goto cchunk_out; - -cifs_dbg (FYI ,"MaxChunks %d BytesChunk %d MaxCopy %d\n" , -le32_to_cpu (retbuf->ChunksWritten), -le32_to_cpu (retbuf->ChunkBytesWritten), -le32_to_cpu (retbuf->TotalBytesWritten)); -/* -* Check if this is the first request using these sizes, -* (ie check if copy succeed once with original sizes -* and check if the server gave us different sizes after -* we already updated max sizes on previous request). -* if not then why is the server returning an error now -*/ -if ((chunks_copied !=0 ) || chunk_sizes_updated) - goto cchunk_out; - -/* Check that server is not asking us to grow size */ -if (le32_to_cpu (retbuf->ChunkBytesWritten) < - tcon->max_bytes_chunk) - tcon->max_bytes_chunk = -le32_to_cpu (retbuf->ChunkBytesWritten); -else - goto cchunk_out;/* server gave us bogus size */ + total_bytes_left -= bytes_written; +continue ; + } -/* No need to change MaxChunks since already set to 1 */ - chunk_sizes_updated = true; - }else - goto cchunk_out; +/* +* Check if server is not asking us to reduce size. +* +* Note: As per MS-SMB2 2.2.32.1, the values returned +* in cc_rsp are not strictly lower than what existed +* before. +*/ +if (bytes_written < tcon->max_bytes_copy) { +cifs_tcon_dbg (FYI ,"Copychunk MaxBytesCopy updated: %u -> %u\n" , + tcon->max_bytes_copy, bytes_written); + tcon->max_bytes_copy = bytes_written; + } + +if (chunks_written < tcon->max_chunks) { +cifs_tcon_dbg (FYI ,"Copychunk MaxChunks updated: %u -> %u\n" , + tcon->max_chunks, chunks_written); + tcon->max_chunks = chunks_written; + } + +if (chunk_bytes < tcon->max_bytes_chunk) { +cifs_tcon_dbg (FYI ,"Copychunk MaxBytesChunk updated: %u -> %u\n" , + tcon->max_bytes_chunk, chunk_bytes); + tcon->max_bytes_chunk = chunk_bytes; + } + +/* reset to last offsets */ +if (retries++ <2 ) { + src_off = src_off_prev; + dst_off = dst_off_prev; +kfree (cc_req); + cc_req =NULL ; + goto retry; + } + +break ; } -cchunk_out: -kfree (pcchunk); -kfree (retbuf); -if (rc) +out: +kfree (cc_req); +kfree (cc_rsp); +if (rc) { +trace_smb3_copychunk_err (xid, src_file->fid.volatile_fid, + dst_file->fid.volatile_fid, tcon->tid, + tcon->ses->Suid, src_off, dst_off, len, rc);return rc; -else -return total_bytes_written; + }else { +trace_smb3_copychunk_done (xid, src_file->fid.volatile_fid, + dst_file->fid.volatile_fid, tcon->tid, + tcon->ses->Suid, src_off, dst_off, len); +return len; + } } static int
fs/smb/client/smb2pdu.h +10 -6
@@ -201,16 +201,20 @@ struct resume_key_req {char Context[];/* ignored, Windows sets to 4 bytes of zero */ } __packed; + +struct copychunk { +__le64 SourceOffset; +__le64 TargetOffset; +__le32 Length; +__le32 Reserved; +} __packed; +/* this goes in the ioctl buffer when doing a copychunk request */ struct copychunk_ioctl {char SourceKey[COPY_CHUNK_RES_KEY_SIZE ]; -__le32 ChunkCount;/* we are only sending 1 */ +__le32 ChunkCount;__le32 Reserved; -/* array will only be one chunk long for us */ -__le64 SourceOffset; -__le64 TargetOffset; -__le32 Length;/* how many bytes to copy */ -__u32 Reserved2; +struct copychunk Chunks[]; } __packed;struct copychunk_ioctl_rsp {
fs/smb/client/trace.h +1 -1
@@ -271,7 +271,7 @@ DEFINE_EVENT(smb3_copy_range_err_class, smb3_##name, \ TP_ARGS(xid, src_fid, target_fid, tid, sesid, src_offset, target_offset, len, rc))DEFINE_SMB3_COPY_RANGE_ERR_EVENT (clone_err); -/* TODO: Add SMB3_COPY_RANGE_ERR_EVENT(copychunk_err) */ +DEFINE_SMB3_COPY_RANGE_ERR_EVENT (copychunk_err); DECLARE_EVENT_CLASS(smb3_copy_range_done_class, TP_PROTO(unsigned int xid,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 528/1102 smb: move copychunk definitions to common/smb2pdu.h
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit cc26f593dc193567bdb059a6ffde58e627a44f65 ]
Rename 3 places:
- copychunk_ioctl -> copychunk_ioctl_req
- copychunk -> srv_copychunk
- server: ResumeKey -> SourceKeyU64
Merge the struct members of the server and the client, then move duplicate definitions to common header file.
Stable-dep-of: 0923ae9f23cc ("smb/client: fix data corruption in emulated insert range")
--- fs/smb/client/cifspdu.h | 8 -------- fs/smb/client/smb2ops.c | 6 +++--- fs/smb/client/smb2pdu.h | 24 ------------------------ fs/smb/common/smb2pdu.h | 29 +++++++++++++++++++++++++++++ fs/smb/server/smb2pdu.c | 4 ++-- fs/smb/server/smb2pdu.h | 20 -------------------- 6 files changed, 34 insertions(+), 57 deletions(-)
fs/smb/client/cifspdu.h +0 -8
@@ -1283,14 +1283,6 @@ typedef struct smb_com_ntransact_rsp {/* parms and data follow */ } __attribute__((packed)) NTRANSACT_RSP; -/* See MS-SMB 2.2.7.2.1.1 */ -struct srv_copychunk { -__le64 SourceOffset; -__le64 DestinationOffset; -__le32 CopyLength; -__u32 Reserved; -} __packed; - typedef struct smb_com_transaction_ioctl_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount;
fs/smb/client/smb2ops.c +3 -3
@@ -1546,7 +1546,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,static int SMB2_request_res_key (const unsigned int xid,struct cifs_tcon *tcon,u64 persistent_fid,u64 volatile_fid, -struct copychunk_ioctl *pcchunk) +struct copychunk_ioctl_req *pcchunk) {int rc;unsigned int ret_data_len;
@@ -1886,10 +1886,10 @@ smb2_copychunk_range(const unsigned int xid, {int rc =0 ;unsigned int ret_data_len =0 ; -struct copychunk_ioctl *cc_req =NULL ; +struct copychunk_ioctl_req *cc_req =NULL ;struct copychunk_ioctl_rsp *cc_rsp =NULL ;struct cifs_tcon *tcon; -struct copychunk *chunk; +struct srv_copychunk *chunk;u32 chunks, chunk_count, chunk_bytes;u32 copy_bytes, copy_bytes_left;u32 chunks_written, bytes_written;
fs/smb/client/smb2pdu.h +0 -24
@@ -193,36 +193,12 @@ struct crt_sd_ctxt {struct smb3_sd sd; } __packed; - -#define COPY_CHUNK_RES_KEY_SIZE 24struct resume_key_req {char ResumeKey[COPY_CHUNK_RES_KEY_SIZE ];__le32 ContextLength;/* MBZ */ char Context[];/* ignored, Windows sets to 4 bytes of zero */ } __packed; - -struct copychunk { -__le64 SourceOffset; -__le64 TargetOffset; -__le32 Length; -__le32 Reserved; -} __packed; - -/* this goes in the ioctl buffer when doing a copychunk request */ -struct copychunk_ioctl { -char SourceKey[COPY_CHUNK_RES_KEY_SIZE ]; -__le32 ChunkCount; -__le32 Reserved; -struct copychunk Chunks[]; -} __packed; - -struct copychunk_ioctl_rsp { -__le32 ChunksWritten; -__le32 ChunkBytesWritten; -__le32 TotalBytesWritten; -} __packed; -/* See MS-FSCC 2.3.29 and 2.3.30 */ struct get_retrieval_pointer_count_req {__le64 StartingVcn;/* virtual cluster number (signed) */
fs/smb/common/smb2pdu.h +29 -0
@@ -1388,6 +1388,35 @@ struct smb2_ioctl_req {__u8 Buffer[]; } __packed; +/* See MS-SMB2 2.2.31.1.1 */ +struct srv_copychunk { +__le64 SourceOffset; +__le64 TargetOffset; +__le32 Length; +__le32 Reserved; +} __packed; + +#define COPY_CHUNK_RES_KEY_SIZE 24 + +/* See MS-SMB2 2.2.31.1 */ +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl_req { +union { +char SourceKey[COPY_CHUNK_RES_KEY_SIZE ]; +__le64 SourceKeyU64[3 ]; + }; +__le32 ChunkCount; +__le32 Reserved; + struct srv_copychunk Chunks[] __counted_by_le(ChunkCount); +} __packed; + +/* See MS-SMB2 2.2.32.1 */ +struct copychunk_ioctl_rsp { +__le32 ChunksWritten; +__le32 ChunkBytesWritten; +__le32 TotalBytesWritten; +} __packed; +struct smb2_ioctl_rsp {struct smb2_hdr hdr;__le16 StructureSize;/* Must be 49 */
fs/smb/server/smb2pdu.c +2 -2
@@ -7927,11 +7927,11 @@ static int fsctl_copychunk(struct ksmbd_work *work, } src_fp =ksmbd_lookup_foreign_fd (work, -le64_to_cpu (ci_req->ResumeKey[0 ])); +le64_to_cpu (ci_req->SourceKeyU64[0 ])); dst_fp =ksmbd_lookup_fd_slow (work, volatile_id, persistent_id); ret = -EINVAL ;if (!src_fp || - src_fp->persistent_id !=le64_to_cpu (ci_req->ResumeKey[1 ])) { + src_fp->persistent_id !=le64_to_cpu (ci_req->SourceKeyU64[1 ])) { rsp->hdr.Status =STATUS_OBJECT_NAME_NOT_FOUND ; goto out; }
fs/smb/server/smb2pdu.h +0 -20
@@ -193,26 +193,6 @@ struct resume_key_ioctl_rsp {__u8 Context[4 ];/* ignored, Windows sets to 4 bytes of zero */ } __packed; -struct srv_copychunk { -__le64 SourceOffset; -__le64 TargetOffset; -__le32 Length; -__le32 Reserved; -} __packed; - -struct copychunk_ioctl_req { -__le64 ResumeKey[3 ]; -__le32 ChunkCount; -__le32 Reserved; - struct srv_copychunk Chunks[] __counted_by_le(ChunkCount); -} __packed; - -struct copychunk_ioctl_rsp { -__le32 ChunksWritten; -__le32 ChunkBytesWritten; -__le32 TotalBytesWritten; -} __packed; -struct file_sparse {__u8 SetSparse; } __packed;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 529/1102 smb/client: fix data corruption in emulated insert range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 0923ae9f23cc9460b0df6fc124cd56ec4436411b ]
smb3_insert_range() shifts [off, EOF) right with COPYCHUNK, copying from low to high offsets. When the ranges overlap, the copy can overwrite source data that has not yet been copied. For a 1 MiB insert at offset 0:
offset: 0 1M 2M 3M 4M 5M
before: | A | B | C | D | expected: | hole | A | B | C | D | current: | hole | A | A | A | A | (corrupted)
Let x be the insertion offset, L the total length to move, delta the insert length, and C the normal chunk size allowed by the server. Insert range maps
[x, x + L) -> [x + delta, x + delta + L).
When delta >= L, the complete source and target ranges are disjoint, so the normal copy order and chunk size are safe:
offset: 0 4 8 12 16 20 24 28 32
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
When delta < L, the complete source and target ranges overlap, so the copy must proceed from EOF backwards. There are two subcases.
If delta >= C, each corresponding source and target chunk is disjoint. The 1 MiB example has L = 4 MiB and delta = C = 1 MiB:
offset: 0 1M 2M 3M 4M 5M
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
Copying S0 from [0, 1M) to [1M, 2M) overwrites S1 before it is copied. Processing chunks from EOF backwards prevents this inter-chunk overwrite.
If delta < C, the source and target ranges of a normal chunk also overlap. For example, with L = 16, delta = 2 and C = 4:
offset: 0 2 4 6 8 10 12 14 16 18
source: [--S0--][--S1--][--S2--][--S3--]
target: [--T0--][--T1--][--T2--][--T3--]
Here S0 and T0 overlap over [2,4), S1 and T1 over [6,8), and so on. Backward ordering cannot control how the server copies bytes inside one descriptor, so the chunk size must be limited to delta.
Fix this by copying overlapping right shifts from EOF backwards. Limit the chunk size to delta when delta < C so that each chunk's source and target ranges do not overlap. Using larger chunks would require a way to identify servers that safely handle overlapping COPYCHUNK descriptors.
Therefore:
delta >= L:
keep the normal copy order and chunk size
delta < L:
delta >= C: copy backwards and keep the normal chunk size delta < C: copy backwards and limit the chunk size to delta
Only the delta < C subcase requires reducing the chunk size for data integrity.
Reproducer:
bash -c '
MNT=/mnt/scratch
# Generate four 1 MiB random blocks: [A][B][C][D]. dd if=/dev/urandom of=/tmp/src bs=1M count=4 status=none
# With C = 1 MiB, test delta = C and delta < C. for delta in 1M 1K; do
truncate -s 0 /tmp/expected truncate -s "$delta" /tmp/expected cat /tmp/src >> /tmp/expected
cp /tmp/src "$MNT/file"
fallocate --insert-range -o 0 -l "$delta" "$MNT/file"
if cmp -s /tmp/expected "$MNT/file"; then
echo "delta=$delta: OK"
else
echo "delta=$delta: CORRUPTED"
fi
done
'
The corruption reproduces with Samba and ksmbd, while Windows handles the overlapping COPYCHUNK ranges safely.
The 1 MiB case tests delta >= C, while the 1 KiB case tests delta < C. Before this change, the reproducer reports:
delta=1M: CORRUPTED
delta=1K: CORRUPTED
After this change, it passes against both ksmbd and Samba:
delta=1M: OK
delta=1K: OK
--- fs/smb/client/smb2ops.c | 132 ++++++++++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 26 deletions(-)
fs/smb/client/smb2ops.c +106 -26
@@ -1838,31 +1838,31 @@ smb2_ioctl_query_info(const unsigned int xid,* * @tcon: destination file tcon * @bytes_left: how many bytes are left to copy +* @chunk_size: maximum size of a single chunk * * Return: maximum number of chunks with which Chunks[] can be filled. */ static inline u32 -calc_chunk_count (struct cifs_tcon *tcon,u64 bytes_left) +calc_chunk_count (struct cifs_tcon *tcon,u64 bytes_left,u32 chunk_size) {u32 max_chunks =READ_ONCE (tcon->max_chunks);u32 max_bytes_copy =READ_ONCE (tcon->max_bytes_copy); -u32 max_bytes_chunk =READ_ONCE (tcon->max_bytes_chunk);u64 need;u32 allowed; -if (!max_bytes_chunk || !max_bytes_copy || !max_chunks) +if (!chunk_size || !max_bytes_copy || !max_chunks)return 0 ;/* chunks needed for the remaining bytes */ - need =DIV_ROUND_UP_ULL (bytes_left, max_bytes_chunk); + need =DIV_ROUND_UP_ULL (bytes_left, chunk_size);/* chunks allowed per cc request */ - allowed =DIV_ROUND_UP (max_bytes_copy, max_bytes_chunk); + allowed =DIV_ROUND_UP (max_bytes_copy, chunk_size);return (u32 )umin (need,umin (max_chunks, allowed)); }/** -* smb2_copychunk_range - server-side copy of data range +* __smb2_copychunk_range - server-side copy of data range * * @xid: transaction id * @src_file: source file
@@ -1874,15 +1874,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE * IOCTLs, splitting the request into chunks limited by tcon->max_*. * -* Return: @len on success; negative errno on failure. +* Return: 0 on success; negative errno on failure. */ -static ssize_t -smb2_copychunk_range (const unsigned int xid, -struct cifsFileInfo *src_file, -struct cifsFileInfo *dst_file, -u64 src_off, -u64 len, -u64 dst_off) +static int +__smb2_copychunk_range (const unsigned int xid, +struct cifsFileInfo *src_file, +struct cifsFileInfo *dst_file, +u64 src_off, +u64 len, +u64 dst_off) {int rc =0 ;unsigned int ret_data_len =0 ;
@@ -1890,12 +1890,14 @@ smb2_copychunk_range(const unsigned int xid,struct copychunk_ioctl_rsp *cc_rsp =NULL ;struct cifs_tcon *tcon;struct srv_copychunk *chunk; -u32 chunks, chunk_count, chunk_bytes; +u32 chunks, chunk_count, chunk_bytes, chunk_size;u32 copy_bytes, copy_bytes_left;u32 chunks_written, bytes_written;u64 total_bytes_left = len;u64 src_off_prev, dst_off_prev; +u64 max_chunk =0 ;u32 retries =0 ; +bool reverse = false; tcon =tlink_tcon (dst_file->tlink);
@@ -1903,8 +1905,50 @@ smb2_copychunk_range(const unsigned int xid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); +/* +* Same-file left shifts are safe in forward order. For a right shift, +* let L be the copy length, delta the distance between the source and +* destination, and C the normal chunk size: +* +* delta >= L: copy forwards using C +* delta < L: +* delta >= C: copy backwards using C +* delta < C: copy backwards with chunks limited to delta +* +* Copying backwards prevents one chunk from overwriting data needed by +* a later chunk. Limiting the chunk size to delta prevents an individual +* chunk from overlapping itself. +* This limit can be removed once all supported servers handle overlapping +* descriptors safely. +* +* A small right shift over a large range may therefore require many +* chunks. +*/ +if (src_file == dst_file && dst_off > src_off) { +u64 delta = dst_off - src_off; + +if (delta < len) { + reverse = true; + max_chunk = delta; + } + } + +/* +* A backward copy walks the offsets down from the end of the range. +* Do this once, outside the retry loop, so a retry does not move the +* offsets again. +*/ +if (reverse) { + src_off += len; + dst_off += len; + } + retry: - chunk_count =calc_chunk_count (tcon, total_bytes_left); + chunk_size =READ_ONCE (tcon->max_bytes_chunk); +if (max_chunk && max_chunk < chunk_size) + chunk_size = (u32 )max_chunk; + + chunk_count =calc_chunk_count (tcon, total_bytes_left, chunk_size);if (!chunk_count) { rc = -EOPNOTSUPP ; goto out;
@@ -1939,16 +1983,21 @@ smb2_copychunk_range(const unsigned int xid,while (copy_bytes_left >0 && chunks < chunk_count) { chunk = &cc_req->Chunks[chunks++]; + chunk_bytes =umin (copy_bytes_left, chunk_size); +if (reverse) { + src_off -= chunk_bytes; + dst_off -= chunk_bytes; + } + chunk->SourceOffset =cpu_to_le64 (src_off); chunk->TargetOffset =cpu_to_le64 (dst_off); - - chunk_bytes =umin (copy_bytes_left, tcon->max_bytes_chunk); - chunk->Length =cpu_to_le32 (chunk_bytes);/* Buffer is zeroed, no need to set chunk->Reserved = 0 */ - src_off += chunk_bytes; - dst_off += chunk_bytes; +if (!reverse) { + src_off += chunk_bytes; + dst_off += chunk_bytes; + } copy_bytes_left -= chunk_bytes; copy_bytes += chunk_bytes;
@@ -1989,6 +2038,18 @@ smb2_copychunk_range(const unsigned int xid, goto out; } +/* +* A successful COPYCHUNK should copy every descriptor (MS-SMB2 +* 3.3.5.15.6). Reject a short backward copy because the rewind +* below only supports forward copying. +*/ +if (unlikely (reverse && bytes_written < copy_bytes)) { +cifs_tcon_dbg (VFS ,"Copychunk short write %u/%u (reverse)\n" , + bytes_written, copy_bytes); + rc = -EIO ; + goto out; + } +/* Partial write: rewind */ if (bytes_written < copy_bytes) {u32 delta = copy_bytes - bytes_written;
@@ -2050,10 +2111,27 @@ smb2_copychunk_range(const unsigned int xid,trace_smb3_copychunk_done (xid, src_file->fid.volatile_fid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); -return len; +return 0 ; } } +static ssize_t +smb2_copychunk_range (const unsigned int xid, +struct cifsFileInfo *src_file, +struct cifsFileInfo *dst_file, +u64 src_off, +u64 len, +u64 dst_off) +{ +int rc; + + rc =__smb2_copychunk_range (xid, src_file, dst_file, src_off, len, + dst_off); +if (rc) +return rc; +return len; +} +static int smb2_flush_file (const unsigned int xid,struct cifs_tcon *tcon,struct cifs_fid *fid)
@@ -3858,7 +3936,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,struct cifsFileInfo *cfile = file->private_data;struct inode *inode =file_inode (file);struct cifsInodeInfo *cifsi =CIFS_I (inode); -u64 count;loff_t old_eof, new_eof; xid =get_xid ();
@@ -3877,8 +3954,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,if (rc) goto out; - count = old_eof - off; -/* SET_ZERO_DATA creates a hole only in a sparse file. */ rc =smb2_set_sparse (xid, tcon, cfile, inode, true);if (rc)
@@ -3900,7 +3975,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,netfs_resize_file (&cifsi->netfs,i_size_read (inode), true);fscache_resize_cookie (cifs_inode_cookie (inode),i_size_read (inode)); - rc =smb2_copychunk_range (xid, cfile, cfile, off, count, off + len); +/* +* Move [off, old_eof) right by len. The helper copies backwards if the +* source and destination ranges overlap. +*/ + rc =__smb2_copychunk_range (xid, cfile, cfile, off, old_eof - off, + off + len);if (rc <0 ) goto out_2; cifsi->netfs.zero_point = new_eof;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 530/1102 smb/client: fix integer truncation in collapse range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 7811701d6af7db76481a82b9bc3c4adf7863acf5 ]
smb3_collapse_range() stores the ssize_t return value of smb2_copychunk_range() in an int. A successful copy larger than INT_MAX is truncated to a negative value and treated as an error.
Reproducer:
MNT=/mnt/scratch truncate -s 2056M "$MNT/file" fallocate --collapse-range -o 1M -l 1M "$MNT/file"
Fix this by using __smb2_copychunk_range(), which reports success as zero instead of returning the copied byte count.
Before this change, the reproducer fails with:
fallocate: fallocate failed: Success
and the file size remains unchanged at 2056 MiB. After this change, the reproducer succeeds and the file size becomes the expected 2055 MiB.
--- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/client/smb2ops.c +2 -2
@@ -3904,8 +3904,8 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, ictx->zero_point = old_eof;netfs_wait_for_outstanding_io (inode); - rc =smb2_copychunk_range (xid, cfile, cfile, off + len, - old_eof - off - len, off); + rc =__smb2_copychunk_range (xid, cfile, cfile, off + len, + old_eof - off - len, off);if (rc <0 ) goto out_2;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 531/1102 smb: client: transport: Fix debug printing in __release_mid()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit d83a21bb26015bfdd79b0440fe816b271b8bbab3 ]
Long time ago during upgrading printk():s to the respective pr_<level>() calls one misconversion happened and nobody has noticed that. So, previously printk(KERN_DEBUG) + printk() worked as one long debug print since the trailing '\n' is only present in the followup printk() format string. The culprit change missed that and split the message to two on the different levels. Restore the original behaviour to make users be less confused in the most likely never happen cases of partially getting that message.
--- fs/smb/client/transport.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
fs/smb/client/transport.c +5 -6
@@ -144,12 +144,11 @@ void __release_mid(struct kref *refcount)trace_smb3_slow_rsp (smb_cmd, midEntry->mid, midEntry->pid, midEntry->when_sent, midEntry->when_received);if (cifsFYI &CIFS_TIMER ) { -pr_debug ("slow rsp: cmd %d mid %llu" , - midEntry->command, midEntry->mid); -cifs_info ("A: 0x%lx S: 0x%lx R: 0x%lx\n" , - now - midEntry->when_alloc, - now - midEntry->when_sent, - now - midEntry->when_received); +pr_debug ("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n" , + midEntry->command, midEntry->mid, + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); } }#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 532/1102 sctp: fix soft lockup from unpadded ASCONF-ACK parameter iteration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henry Martin <[email protected]>
[ Upstream commit 2cb0b0b1ed69430bf73740377ea0a1c44c50db63 ]
sctp_verify_asconf() walks ASCONF-ACK parameters with sctp_walk_params(), which advances by SCTP_PAD4(length), while the consumer sctp_get_asconf_response() iterates the same parameters advancing by the raw length, without padding. A single odd-length parameter desynchronises the two walks and makes the consumer interpret attacker-controlled bytes at a misaligned offset.
When those bytes yield a length of zero, the while loop over asconf_ack_len makes no progress, spinning forever in softirq context, and the watchdog reports a soft lockup. All reads stay within the received skb, so the lockup is a pure remote denial of service. A remote peer can trigger it with a crafted ASCONF-ACK on an ADD-IP enabled association with an outstanding ASCONF (RFC 5061 section 4.1.2 requires the chunk to be authenticated, but the predefined empty key id 0 allows the peer to compute the same association HMAC from publicly exchanged parameters, so the gate does not help).
The SCTP_PARAM_ERR_CAUSE case of sctp_verify_asconf() also performs no length check, letting a parameter without a complete error header reach the consumer, which reads errhdr.cause past the end of the parameter, an out-of-bounds read.
Reject SCTP_PARAM_ERR_CAUSE parameters shorter than sizeof(struct sctp_addip_param) + sizeof(struct sctp_errhdr) at the verifier, and advance the consumer iterator with the same padding rule as the verifier to keep the two walks in lockstep. The verifier change guarantees a complete error header in every ERR_CAUSE parameter the consumer can see, so the consumer's asconf_ack_len check is dropped and it returns err_param->cause directly. The consumer padding fix is still required because odd lengths remain valid for SCTP_PARAM_ERR_CAUSE per RFC 5061.
The issue was found by ZeroHive, a vulnerability hunting agent at Tencent Yunding Lab.
--- net/sctp/sm_make_chunk.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
net/sctp/sm_make_chunk.c +6 -8
@@ -3230,6 +3230,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, *errp = param.p;switch (param.p->type) {case SCTP_PARAM_ERR_CAUSE : +if (length <sizeof (struct sctp_addip_param ) + +sizeof (struct sctp_errhdr )) +return false;break ;case SCTP_PARAM_IPV4_ADDRESS :if (length !=sizeof (struct sctp_ipv4addr_param ))
@@ -3463,20 +3466,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,case SCTP_PARAM_ERR_CAUSE : length =sizeof (*asconf_ack_param); err_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; -if (asconf_ack_len >0 ) -return err_param->cause; -else -return SCTP_ERROR_INV_PARAM ; -break ; +return err_param->cause;default :return SCTP_ERROR_INV_PARAM ; } } length =ntohs (asconf_ack_param->param_hdr.length); - asconf_ack_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; + asconf_ack_param = (void *)asconf_ack_param +SCTP_PAD4 (length); + asconf_ack_len -=SCTP_PAD4 (length); }return err_code;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 533/1102 raw: annotate disconnect-side IPv4 match writers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
[ Upstream commit ac08d183dac0441e41f77bbad50798fe609d90f1 ]
raw_v4_match() reads inet_daddr, inet_rcv_saddr and sk_bound_dev_if locklessly under RCU. Bind and connect writers are annotated, but __udp_disconnect() still clears the same fields using plain stores.
Commit 18f116931f52e ("raw: annotate lockless match fields in raw_v4_match()") added the lockless readers and annotated the raw bind and datagram connect writers. Its v4 revision intentionally left the shared disconnect-side IPv4 writers for follow-up cleanup.
Complete that follow-up by using WRITE_ONCE() for the disconnect-side stores, including the inet_rcv_saddr reset in inet_reset_saddr(), to pair with the lockless raw socket matcher.
--- include/net/ip.h | 3 ++- net/ipv4/udp.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-)
include/net/ip.h +2 -1
@@ -689,7 +689,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,static __inline__void inet_reset_saddr (struct sock *sk) { -inet_sk (sk)->inet_rcv_saddr =inet_sk (sk)->inet_saddr =0 ; +inet_sk (sk)->inet_saddr =0 ; +WRITE_ONCE (inet_sk (sk)->inet_rcv_saddr,0 );#if IS_ENABLED (CONFIG_IPV6 )if (sk->sk_family ==PF_INET6 ) {struct ipv6_pinfo *np =inet6_sk (sk);
net/ipv4/udp.c +2 -2
@@ -2006,10 +2006,10 @@ int __udp_disconnect(struct sock *sk, int flags)*/ sk->sk_state =TCP_CLOSE ; - inet->inet_daddr =0 ; +WRITE_ONCE (inet->inet_daddr,0 ); inet->inet_dport =0 ;sock_rps_reset_rxhash (sk); - sk->sk_bound_dev_if =0 ; +WRITE_ONCE (sk->sk_bound_dev_if,0 );if (!(sk->sk_userlocks &SOCK_BINDADDR_LOCK )) {inet_reset_saddr (sk); if (sk->sk_prot->rehash &&
Signature · 1 line
2.53.0
Re: [PATCH 6.12 0533/1102] raw: annotate disconnect-side IPv4 match writers
Quoted 8 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
[ Upstream commit
ac08d183dac0441e41f77bbad50798fe609d90f1]
Quoted 7 lines
raw_v4_match()readsinet_daddr,inet_rcv_saddrandsk_bound_dev_iflocklessly under RCU. Bind and connect writers are annotated, but__udp_disconnect()still clears the same fields using plain stores.Commit
18f116931f52e("raw: annotate lockless match fields inraw_v4_match()")
^^^
Quoted 41 lines
added the lockless readers and annotated the raw bind
and datagram connect writers. Its v4 revision intentionally left the shared disconnect-side IPv4 writers for follow-up cleanup.Complete that follow-up by using
WRITE_ONCE()for the disconnect-side stores, including theinet_rcv_saddrreset ininet_reset_saddr(), to pair with the lockless raw socket matcher.Fixes 0daf07e52709 ("raw: convert raw sockets to RCU")Suggested-by Runyu Xiao <[email protected]>Signed-off-by Jackie Liu <[email protected]>Signed-off-by Xuanqiang Luo <[email protected]>Reviewed-by Eric Dumazet <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- include/net/ip.h | 3 ++- net/ipv4/udp.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-)include/net/ip.h +2
-1@@ -689,7 +689,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,static __inline__void inet_reset_saddr (struct sock *sk) { -inet_sk (sk)->inet_rcv_saddr =inet_sk (sk)->inet_saddr =0 ; +inet_sk (sk)->inet_saddr =0 ; +WRITE_ONCE (inet_sk (sk)->inet_rcv_saddr,0 );#if IS_ENABLED (CONFIG_IPV6 )if (sk->sk_family ==PF_INET6 ) {struct ipv6_pinfo *np =inet6_sk (sk);net/ipv4/udp.c +0
-0
net/ipv4/udp.c int __udp_disconnect(struct sock *sk, int flags) · line 2009
Quoted 5 lines
net/ipv4/udp.c +1
-1@@ -2006,10 +2006,10 @@ int __udp_disconnect(struct sock *sk, int flags)*/ sk->sk_state =TCP_CLOSE ; - inet->inet_daddr =0 ; +WRITE_ONCE (inet->inet_daddr,0 );
^^^
Hi Greg/Sasha,
I ran an AI-assisted review for 843d6af89a11 against the 6.12.y raw IPv4 paths.
Upstream ac08d183dac0 explicitly follows and completes:
18f116931f52e ("raw: annotate lockless match fields in raw_v4_match()")
With that predecessor, upstream raw_v4_match() uses:
daddr = READ_ONCE(inet->inet_daddr);
rcv_saddr = READ_ONCE(inet->inet_rcv_saddr);
return raw_sk_bound_dev_eq(net,
READ_ONCE(sk->sk_bound_dev_if),
dif, sdif);It also uses WRITE_ONCE() for the raw bind and IPv4 datagram connect writers. ac08d183dac0 adds the remaining disconnect-side stores:
WRITE_ONCE(inet->inet_daddr, 0);
WRITE_ONCE(sk->sk_bound_dev_if, 0);
WRITE_ONCE(inet_sk(sk)->inet_rcv_saddr, 0);However, 6.12.y still reads these fields with plain accesses:
!(inet->inet_daddr && inet->inet_daddr != raddr) &&
!(inet->inet_rcv_saddr &&
inet->inet_rcv_saddr != laddr) &&
raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif)Its bind and connect paths also retain plain stores. Therefore, 843d6af89a11 alone does not complete the intended lockless-access contract, and the raw-socket matching race remains. The partial WRITE_ONCE() conversion does not appear to introduce a new regression.
Maybewe should take an adapted version of 18f116931f52 with 843d6af89a11. If that prerequisite is unsuitable for 6.12.y, dropping the standalone follow-up would avoid carrying an incomplete fix.
thanks,
Harshit> inet->inet_dport = 0;
Quoted 6 lines
net/ipv4/udp.c +1
-1sock_rps_reset_rxhash (sk); - sk->sk_bound_dev_if =0 ; +WRITE_ONCE (sk->sk_bound_dev_if,0 );if (!(sk->sk_userlocks &SOCK_BINDADDR_LOCK )) {inet_reset_saddr (sk); if (sk->sk_prot->rehash &&
Sasha Levin
maintainerRe: [PATCH 6.12 0533/1102] raw: annotate disconnect-side IPv4 match writers
Quoted 8 lines
Therefore,
843d6af89a11alone does not complete the intended lockless-access contract, and the raw-socket matching race remains. The partialWRITE_ONCE()conversion does not appear to introduce a new regression.Maybewe should take an adapted version of
18f116931f52with843d6af89a11. If that prerequisite is unsuitable for 6.12.y, dropping the standalone follow-up would avoid carrying an incomplete fix.
The prerequisite is not cheap: upstream reordered __ip4_datagram_connect() to write inet_daddr first, so it conflicts in net/ipv4/datagram.c on all three trees and in net/ipv4/raw.c on 6.1. Three hand resolutions for an annotation-only change with no Cc: stable on either side.
Dropping the follow-up from the 6.12, 6.6 and 6.1 rounds is the better trade, as you suggest.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 534/1102 net: amd-xgbe: discard rx packets with bad FCS
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Nugraha <[email protected]>
[ Upstream commit ac8d6b28d48c5d951dcd923d33e461588e762a6d ]
amd-xgbe driver currently sets the MAC_RCR.DCRCC bit whenever RX is enabled. This disables hardware FCS validation, causing packets with bad FCS to be accepted unconditionally.
This change unsets DCRCC so that packets with bad FCS will be dropped, in-line with typical behaviours of many other network controllers.
Tests:
- Verified that packets with bad FCS are now dropped.
- Verified that receiving packets with bad FCS will increment the rx_crc_errors counter.
--- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/amd/xgbe/xgbe-dev.c +1 -2
@@ -3438,7 +3438,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata)XGMAC_IOWRITE (pdata,MAC_RQC0R , reg_val);/* Enable MAC Rx */ -XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,1 ); +XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,CST ,1 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,ACS ,1 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,RE ,1 );
@@ -3449,7 +3449,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata)unsigned int i;/* Disable MAC Rx */ -XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,DCRCC ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,CST ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,ACS ,0 );XGMAC_IOWRITE_BITS (pdata,MAC_RCR ,RE ,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 535/1102 vxlan: mdb: Fix use-after-free in vxlan_mdb_remote_src_del()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
[ Upstream commit 4aa61c88b4e292e10abdfd791334b8272108d68a ]
vxlan_mdb_is_valid_source(), which validates MDBE_ATTR_SOURCE and every MDBE_ATTR_SRC_LIST member, accepts the all-zeros address.
A source list is only accepted on a (*, G) entry, whose source is the all-zeros address, and for each member of the list an (S, G) entry is derived from it by substituting the source. Entries are keyed by a plain memcmp() of struct vxlan_mdb_entry_key, so if MDBE_ATTR_SOURCE is present and holds the all-zeros address and the source list holds it as well, the derived (S, G) key is byte-identical to the (*, G) key and resolves to the same entry. Omitting MDBE_ATTR_SOURCE is not equivalent, as the key is then left with a zero address family.
vxlan_mdb_remote_src_del() removes the forwarding entry of a source before freeing the source entry:
vxlan_mdb_remote_src_fwd_del(vxlan, group, remote, &ent->addr); vxlan_mdb_remote_src_entry_del(ent);
With the keys aliased, the first call deletes the remote of the entry that owns 'ent' instead of a separate (S, G) entry, and frees 'ent'. The second call then runs on the freed entry, and its hlist_del() reads ->pprev and ->next out of it and writes through them.
Adding the (*, G) entry with NLM_F_REPLACE and no source list marks the all-zeros source for deletion and reaches this from the sweep at the end of vxlan_mdb_remote_srcs_replace().
BUG: KASAN: slab-use-after-free in __vxlan_mdb_add+0x1cd/0xd70 Read of size 8 at addr ffff888102852500 by task poc/84 __vxlan_mdb_add+0x1cd/0xd70 vxlan_mdb_add+0xc0/0x140 rtnl_mdb_add+0x157/0x2a0 rtnetlink_rcv_msg+0x207/0x5a0 Allocated by task 84: __kmalloc_cache_noprof+0x153/0x360 vxlan_mdb_remote_srcs_add+0x2eb/0x440 __vxlan_mdb_add+0x803/0xd70 Freed by task 84: kfree+0x14c/0x3b0 vxlan_mdb_remote_del+0x129/0x1a0 __vxlan_mdb_del+0x4f/0xe0 vxlan_mdb_remote_src_fwd_del.isra.0+0x162/0x1b0 __vxlan_mdb_add+0x1c5/0xd70
The MDB operations are netns-scoped, so an unprivileged user can perform them in a new user and network namespace.
Reject the all-zeros address in vxlan_mdb_is_valid_source(), which covers both call sites. A (*, G) entry is expressed by omitting the source, so nothing legitimate is refused.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- drivers/net/vxlan/vxlan_mdb.c | 8 ++++++++ tools/testing/selftests/net/test_vxlan_mdb.sh | 6 ++++++ 2 files changed, 14 insertions(+)
drivers/net/vxlan/vxlan_mdb.c +8 -0
@@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,NL_SET_ERR_MSG_MOD (extack,"IPv4 multicast source address is not allowed" );return false; } +if (ipv4_is_zeronet (nla_get_in_addr (attr))) { +NL_SET_ERR_MSG_MOD (extack,"IPv4 all-zeros source address is not allowed" ); +return false; + }break ;#if IS_ENABLED (CONFIG_IPV6 )case htons (ETH_P_IPV6 ): {
@@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,NL_SET_ERR_MSG_MOD (extack,"IPv6 multicast source address is not allowed" ); return false; } +if (ipv6_addr_any (&src)) { +NL_SET_ERR_MSG_MOD (extack,"IPv6 all-zeros source address is not allowed" ); +return false; + }break ; }#endif
tools/testing/selftests/net/test_vxlan_mdb.sh +6 -0
@@ -685,6 +685,9 @@ star_g_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010" log_test $? 255 "Invalid source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010" + log_test $? 255 "All-zeros source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010" log_test $? 255 "Source list without filter mode" }
@@ -784,6 +787,9 @@ sg_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010" log_test $? 255 "(S, G) with an invalid source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010" + log_test $? 255 "(S, G) with an all-zeros source" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010" log_test $? 255 "All-zeros group with source" }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 536/1102 watchdog: msc313e: Fix NULL pointer dereference in PM callbacks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tzung-Bi Shih <[email protected]>
[ Upstream commit e3eceb76515910746e6268c4e4ac1c07516ebd7b ]
msc313e_wdt_probe() doesn't set the driver data for the platform device. As a result, dev_get_drvdata() in msc313e_wdt_suspend() and msc313e_wdt_resume() will return NULL, leading to a NULL pointer dereference afterward.
Set the platform device driver data in msc313e_wdt_probe().
--- drivers/watchdog/msc313e_wdt.c | 1 + 1 file changed, 1 insertion(+)
drivers/watchdog/msc313e_wdt.c +1 -0
@@ -125,6 +125,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)set_bit (WDOG_HW_RUNNING , &priv->wdev.status);watchdog_set_drvdata (&priv->wdev, priv); +platform_set_drvdata (pdev, priv);watchdog_init_timeout (&priv->wdev, timeout, dev);watchdog_stop_on_reboot (&priv->wdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 537/1102 perf symbol: Do not use debug file as the binary type
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Hunter <[email protected]>
[ Upstream commit ae9464c65e9d1ad4df4fed516cee9bca3bc614cc ]
dso__load() sets the binary type of a DSO to the type of the first symbol source found. For a DSO with a separate debug file linked via .gnu-debuglink, that is DSO_BINARY_TYPE__DEBUGLINK, which makes dso__get_filename() return the name of the debug file instead of the file that was actually executed.
Consumers that need to read instruction bytes, such as Intel PT decoding in 'perf script', then read from the debug file and produce wrong instructions.
Prefer DSO_BINARY_TYPE__BUILD_ID_CACHE, and otherwise DSO_BINARY_TYPE__SYSTEM_PATH_DSO, over debug-only types, which restores the behaviour of using a file that contains the executed instructions.
This is a workaround. Properly separating the binary file used for instructions from the file used for debug symbols is left for later.
Example:
Create a shared object with a separate .gnu_debuglink debug file. Note that 'objcopy --only-keep-debug' leaves .text as NOBITS, so instructions read from the debug file are zeros:
# cat > foo.c << EOF
unsigned long foo_work(unsigned long n)
{
unsigned long s = 0; for (unsigned long i = 0; i < n; i++)
s = s * 31 + i;
return s;
}
EOF
# cat > main.c << EOF
#include <stdio.h>
unsigned long foo_work(unsigned long n);
int main(void)
{
printf("%lu\n", foo_work(1000));
return 0;
}
EOF
# gcc -g -O2 -shared -fPIC -o libfoo.so foo.c
# gcc -g -O2 -o main main.c -L. -lfoo -Wl,-rpath,'$ORIGIN'
# objcopy --only-keep-debug libfoo.so libfoo.so.debug
# objcopy --strip-debug libfoo.so
# objcopy --add-gnu-debuglink=libfoo.so.debug libfoo.so
# perf record -e intel_pt//u ./main Note that branch samples must be requested, because it is the resolving of the branch target symbol that causes dso__load() to be called, and hence the binary type to be set, before the decoder walks the code. With '--itrace=e' alone, nothing loads symbols for libfoo.so, the binary type is left as DSO_BINARY_TYPE__NOT_FOUND, the correct file is read anyway, and no errors are reported either way.
Before:
# perf.before script --itrace=be 2>&1 | grep "instruction trace error" instruction trace error type 1 time 2350.467489498 cpu 9 pid 75634 tid 75634 ip 0x77d48480718f code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467489832 cpu 9 pid 75634 tid 75634 ip 0x77d484807341 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467496412 cpu 9 pid 75634 tid 75634 ip 0x5b4de37a8074 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467593393 cpu 9 pid 75634 tid 75634 ip 0x77d4848070d0 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467593954 cpu 9 pid 75634 tid 75634 ip 0x77d4848075a8 code 6: Trace doesn't match instruction instruction trace error type 1 time 2350.467595728 cpu 9 pid 75634 tid 75634 ip 0x77d4848324de code 6: Trace doesn't match instruction 6 instruction trace errors
After:
# perf script --itrace=be 2>&1 | grep "instruction trace error" #
--- tools/perf/util/symbol.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
tools/perf/util/symbol.c +10 -1
@@ -1933,7 +1933,16 @@ int dso__load(struct dso *dso, struct map *map)if (next_slot) { ss_pos++; -if (dso__binary_type (dso) ==DSO_BINARY_TYPE__NOT_FOUND ) +/* +* The binary type is used to find the file containing +* the executed instructions, so prefer the types that +* refer to the actual object over debug-only files such +* as DSO_BINARY_TYPE__DEBUGLINK. +*/ +if (dso__binary_type (dso) ==DSO_BINARY_TYPE__NOT_FOUND || + symtab_type ==DSO_BINARY_TYPE__BUILD_ID_CACHE || + (symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_DSO && +dso__binary_type (dso) !=DSO_BINARY_TYPE__BUILD_ID_CACHE ))dso__set_binary_type (dso, symtab_type);if (syms_ss && runtime_ss)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 538/1102 OPP: of: Fix potential multiplication overflow when calculating freq
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Colin Ian King <[email protected]>
[ Upstream commit e11811a552252740bd396ec38378e9570ee16578 ]
The multiplication be32_to_cpup(val++) * 1000 is performed using 32 bit unsigned integers and hence uses a 32 bit multiplication; this will overflow if be32_to_cpup(val++) is greater than 4294967 (which is very unlikely at present). The result is assigned to an unsigned long (which is a 64 bit value on 64 bit systems), so fix this potential overflow by casting the first operand of the multiplication to an unsigned int.
--- drivers/opp/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/opp/of.c +1 -1
@@ -1107,7 +1107,7 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) val = prop->value;while (nr) { -unsigned long freq =be32_to_cpup (val++) *1000 ; +unsigned long freq = (unsigned long )be32_to_cpup (val++) *1000 ;unsigned long volt =be32_to_cpup (val++);struct dev_pm_opp_data data = { .freq = freq,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 539/1102 ALSA: pcm: Serialize PCM mmap with buffer reallocation to fix page UAF
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yilin Zhang <[email protected]>
[ Upstream commit 9b110a9dcecc59516c77cb3c0caf1f492f75df2d ]
snd_pcm_hw_params() and snd_pcm_hw_free() guard buffer reallocation with an mmap_count check performed under the PCM stream lock, but the lock is released long before the buffer is actually freed:snd_pcm_sync_stop(), constraint refinement and do_free_pages() all happen in between. snd_pcm_mmap_data(), on the other hand, takes no lock at all: it validates against the old buffer's state and dma_bytes, remaps its pages into the VMA, and only then increments mmap_count.
A concurrent mmap() can therefore slip in between the check and the free. remap_pfn_range() installs writable PTEs for the old buffer's pages without taking page references, and the subsequent do_free_pages() returns those pages to the page allocator while the VMA still maps them. This leaves a stale, writable mapping of freed
pages: a page-level use-after-free that can be leveraged for local privilege escalation.
Make snd_pcm_mmap_data() participate in the buffer-access scheme introduced for hw_params/hw_free: acquire runtime->buffer_accessing before validating and remapping, and release it afterwards. Buffer reallocation already fails with -EBUSY while accessors are active, and the mmap side now fails with -EBUSY while a reallocation is in progress, so the validate/remap sequence and the check/free sequence can no longer interleave.
A reproducer that turns this race into a stale writable mapping of the freed DMA buffer pages is available on request.
--- sound/core/pcm_native.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-)
sound/core/pcm_native.c +25 -10
@@ -3945,20 +3945,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,return -EINVAL ; } runtime = substream->runtime; -if (runtime->state ==SNDRV_PCM_STATE_OPEN ) -return -EBADFD ; -if (!(runtime->info &SNDRV_PCM_INFO_MMAP )) -return -ENXIO ; +/* don't race with buffer reallocation in hw_params/hw_free */ +if (!atomic_inc_unless_negative (&runtime->buffer_accessing)) +return -EBUSY ; +if (runtime->state ==SNDRV_PCM_STATE_OPEN ) { + err = -EBADFD ; + goto out; + } +if (!(runtime->info &SNDRV_PCM_INFO_MMAP )) { + err = -ENXIO ; + goto out; + }if (runtime->access ==SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access ==SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) -return -EINVAL ; + runtime->access ==SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ) { + err = -EINVAL ; + goto out; + } size = area->vm_end - area->vm_start; offset = area->vm_pgoff <<PAGE_SHIFT ; dma_bytes =PAGE_ALIGN (runtime->dma_bytes); -if ((size_t )size > dma_bytes) -return -EINVAL ; -if (offset > dma_bytes - size) -return -EINVAL ; +if ((size_t )size > dma_bytes) { + err = -EINVAL ; + goto out; + } +if (offset > dma_bytes - size) { + err = -EINVAL ; + goto out; + } area->vm_ops = &snd_pcm_vm_ops_data; area->vm_private_data = substream;
@@ -3968,6 +3981,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, err =snd_pcm_lib_default_mmap (substream, area);if (!err)atomic_inc (&substream->mmap_count); +out: +atomic_dec (&runtime->buffer_accessing);return err; }EXPORT_SYMBOL (snd_pcm_mmap_data);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 540/1102 ksmbd: propagate DACL parsing errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit c61dc7b1b4a3234b4aa3965502908a292238805c ]
parse_dacl() silently accepts truncated ACEs and allocation failures, allowing set_info_sec() to continue with an incomplete ACL conversion.
Return parsing and allocation errors to parse_sec_desc() so malformed security descriptors are rejected before inode attributes or ACL xattrs are updated.
--- fs/smb/server/smbacl.c | 63 +++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 25 deletions(-)
fs/smb/server/smbacl.c +38 -25
@@ -380,10 +380,10 @@ void free_acl_state(struct posix_acl_state *state)kfree (state->groups); } -static void parse_dacl (struct mnt_idmap *idmap, -struct smb_acl *pdacl,char *end_of_acl, -struct smb_sid *pownersid,struct smb_sid *pgrpsid, -struct smb_fattr *fattr) +static int parse_dacl (struct mnt_idmap *idmap, +struct smb_acl *pdacl,char *end_of_acl, +struct smb_sid *pownersid,struct smb_sid *pgrpsid, +struct smb_fattr *fattr) {int i, ret;u16 num_aces =0 ;
@@ -397,13 +397,13 @@ static void parse_dacl(struct mnt_idmap *idmap,bool owner_found = false, group_found = false, others_found = false;if (!pdacl) -return ; +return 0 ;/* validate that we do not go past end of acl */ if (end_of_acl < (char *)pdacl +sizeof (struct smb_acl ) || end_of_acl < (char *)pdacl +le16_to_cpu (pdacl->size)) {pr_err ("ACL too small to parse DACL\n" ); -return ; +return -EINVAL ; } ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
@@ -415,31 +415,31 @@ static void parse_dacl(struct mnt_idmap *idmap, num_aces =le16_to_cpu (pdacl->num_aces);if (num_aces <=0 ) -return ; +return 0 ; dacl_size =le16_to_cpu (pdacl->size);if (dacl_size <sizeof (struct smb_acl )) -return ; +return -EINVAL ;if (num_aces > (dacl_size -sizeof (struct smb_acl )) / (offsetof(struct smb_ace , sid) + offsetof(struct smb_sid , sub_auth) +sizeof (__le16))) -return ; +return -EINVAL ; ret =init_acl_state (&acl_state, num_aces);if (ret) -return ; +return ret; ret =init_acl_state (&default_acl_state, num_aces);if (ret) {free_acl_state (&acl_state); -return ; +return ret; } ppace =kmalloc_array (num_aces,sizeof (struct smb_ace *),KSMBD_DEFAULT_GFP );if (!ppace) {free_acl_state (&default_acl_state);free_acl_state (&acl_state); -return ; +return -ENOMEM ; }/*
@@ -448,8 +448,10 @@ static void parse_dacl(struct mnt_idmap *idmap,* user/group/other have no permissions */ for (i =0 ; i < num_aces; ++i) { -if (end_of_acl - acl_base < acl_size) -break ; +if (end_of_acl - acl_base < acl_size) { + ret = -EINVAL ; + goto out; + } ppace[i] = (struct smb_ace *)(acl_base + acl_size); acl_base = (char *)ppace[i];
@@ -462,8 +464,10 @@ static void parse_dacl(struct mnt_idmap *idmap, (end_of_acl - acl_base < acl_size +sizeof (__le32) * ppace[i]->sid.num_subauth) || (le16_to_cpu (ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) { + ret = -EINVAL; + goto out; + } acl_size =le16_to_cpu (ppace[i]->size); ppace[i]->access_req =
@@ -538,7 +542,6 @@ static void parse_dacl(struct mnt_idmap *idmap, ((acl_mode &0700 ) >>6 ) |0004 ; } } -kfree (ppace);if (owner_found) {/* The owner must be set to at least read-only. */
@@ -581,10 +584,12 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_acls =posix_acl_alloc (acl_state.users->n + acl_state.groups->n +4 ,KSMBD_DEFAULT_GFP ); -if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; -posix_state_to_acl (&acl_state, cf_pace); +if (!fattr->cf_acls) { + ret = -ENOMEM ; + goto out; } + cf_pace = fattr->cf_acls->a_entries; +posix_state_to_acl (&acl_state, cf_pace); } }
@@ -595,14 +600,20 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_dacls =posix_acl_alloc (default_acl_state.users->n + default_acl_state.groups->n +4 ,KSMBD_DEFAULT_GFP ); -if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; -posix_state_to_acl (&default_acl_state, cf_pdace); +if (!fattr->cf_dacls) { + ret = -ENOMEM ; + goto out; } + cf_pdace = fattr->cf_dacls->a_entries; +posix_state_to_acl (&default_acl_state, cf_pdace); } } + ret =0 ; +out: +kfree (ppace);free_acl_state (&acl_state);free_acl_state (&default_acl_state); +return ret; } static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
@@ -963,8 +974,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,if (dacloffset <sizeof (struct smb_ntsd ))return -EINVAL ; -parse_dacl (idmap, dacl_ptr, end_of_acl, - owner_sid_ptr, group_sid_ptr, fattr); + rc =parse_dacl (idmap, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); +if (rc) +return rc; }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 541/1102 ksmbd: rate limit unmapped SID errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit feca5e70fc963b088377b20879e8cd8237c2fd7d ]
A client can include many structurally valid but unmapped SIDs in a DACL. Logging every mapping failure lets one request generate hundreds of kernel error messages.
Rate limit the message to prevent an authenticated client from flooding the kernel log.
--- fs/smb/server/smbacl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/server/smbacl.c +2 -2
@@ -525,8 +525,8 @@ static int parse_dacl(struct mnt_idmap *idmap, temp_fattr.cf_uid =INVALID_UID ; ret =sid_to_id (idmap, &ppace[i]->sid,SIDOWNER , &temp_fattr);if (ret ||uid_eq (temp_fattr.cf_uid,INVALID_UID )) { -pr_err ("%s: Error %d mapping Owner SID to uid\n" , - __func__, ret); +pr_err_ratelimited ("%s: Error %d mapping Owner SID to uid\n" , + __func__, ret);continue ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 542/1102 s390/vtime: Use __this_cpu_read() / get rid of READ_ONCE()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiko Carstens <[email protected]>
[ Upstream commit e282ccd308de7b2a65eb4596da8030c18b543071 ]
do_account_vtime() runs always with interrupts disabled, therefore use __this_cpu_read() instead of this_cpu_read() to get rid of a pointless preempt_disable() / preempt_enable() pair.
Also there are no concurrent writers to the cpu time accounting fields in lowcore. Therefore get rid of READ_ONCE() usages.
Stable-dep-of: ca1f4a5ecab0 ("s390/time: Use jiffies instead of jiffies_64")
--- arch/s390/kernel/vtime.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-)
arch/s390/kernel/vtime.c +7 -14
@@ -137,23 +137,16 @@ static int do_account_vtime(struct task_struct *tsk) lc->system_timer += timer;/* Update MT utilization calculation */ -if (smp_cpu_mtid && -time_after64 (jiffies_64,this_cpu_read (mt_scaling_jiffies))) +if (smp_cpu_mtid &&time_after64 (jiffies_64,__this_cpu_read (mt_scaling_jiffies)))update_mt_scaling ();/* Calculate cputime delta */ - user =update_tsk_timer (&tsk->thread.user_timer, -READ_ONCE (lc->user_timer)); - guest =update_tsk_timer (&tsk->thread.guest_timer, -READ_ONCE (lc->guest_timer)); - system =update_tsk_timer (&tsk->thread.system_timer, -READ_ONCE (lc->system_timer)); - hardirq =update_tsk_timer (&tsk->thread.hardirq_timer, -READ_ONCE (lc->hardirq_timer)); - softirq =update_tsk_timer (&tsk->thread.softirq_timer, -READ_ONCE (lc->softirq_timer)); - lc->steal_timer += - clock - user - guest - system - hardirq - softirq; + user =update_tsk_timer (&tsk->thread.user_timer, lc->user_timer); + guest =update_tsk_timer (&tsk->thread.guest_timer, lc->guest_timer); + system =update_tsk_timer (&tsk->thread.system_timer, lc->system_timer); + hardirq =update_tsk_timer (&tsk->thread.hardirq_timer, lc->hardirq_timer); + softirq =update_tsk_timer (&tsk->thread.softirq_timer, lc->softirq_timer); + lc->steal_timer += clock - user - guest - system - hardirq - softirq;/* Push account value */ if (user) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 543/1102 s390/time: Use jiffies instead of jiffies_64
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiko Carstens <[email protected]>
[ Upstream commit ca1f4a5ecab084af7f405baa902edbed171b57e6 ]
Christoph Schlameuss and Alexander Egorenkov reported a data-race reported by KCSAN when jiffies_64 is read:
==================================================================
BUG: KCSAN: data-race in do_account_vtime / tick_do_update_jiffies64
write to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 6:
tick_do_update_jiffies64+0x140/0x250
=============================================================>
BUG: KCSAN: data-race in do_account_vtime / tick_do_update_ji>
write to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 6:
tick_do_update_jiffies64+0x140/0x250 tick_nohz_handler+0x2e6/0x300 __run_hrtimer+0x156/0x4d0 __hrtimer_run_queues+0xd2/0x150
...
system_call+0x72/0x90
read to 0x0000016599ea8600 of 8 bytes by interrupt on cpu 12:
do_account_vtime+0x7d6/0x860 vtime_flush+0x26/0xe0 update_process_times+0x32/0x160 tick_nohz_handler+0x12a/0x300
...
system_call+0x72/0x90
value changed: 0x00000000ffffaa6c -> 0x00000000ffffaa6d
...
=============================================================>
Problem is that jiffies_64 instead of jiffies is used. Both are at the same address, but only jiffies is of volatile type, which prevents this warning.
Change the vtime code so jiffies instead of jiffies_64 is used everywhere. This addresses also the inconsistency that both jiffies and jiffies_64 were used in the original patch which introduced this.
--- arch/s390/kernel/vtime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/s390/kernel/vtime.c +3 -3
@@ -31,7 +31,7 @@ static atomic64_t virt_timer_elapsed;DEFINE_PER_CPU (u64, mt_cycles[8 ]); static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; -static DEFINE_PER_CPU(u64, mt_scaling_jiffies); +static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies);static inline void set_vtimer (u64 expires) {
@@ -81,7 +81,7 @@ static void update_mt_scaling(void)memcpy (cycles_old, cycles_new,sizeof (u64) * (smp_cpu_mtid +1 )); } -__this_cpu_write (mt_scaling_jiffies, jiffies_64); +__this_cpu_write (mt_scaling_jiffies, jiffies); }static inline u64 update_tsk_timer (unsigned long *tsk_vtime,u64 new)
@@ -137,7 +137,7 @@ static int do_account_vtime(struct task_struct *tsk) lc->system_timer += timer;/* Update MT utilization calculation */ -if (smp_cpu_mtid &&time_after64 (jiffies_64,__this_cpu_read (mt_scaling_jiffies))) +if (smp_cpu_mtid &&time_after (jiffies,__this_cpu_read (mt_scaling_jiffies)))update_mt_scaling ();/* Calculate cputime delta */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 544/1102 s390/ipl: Fix NULL deref in kdump without re-IPL parm block
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit 7f918871112e8e7c581e99eb8e545af4e59c8367 ]
Some IPL types, like HMC FTP boot or QEMU direct kernel boot, might not provide an IPL parameter block. In this case, reipl_type_init() selects IPL_TYPE_UNKNOWN, and reipl_block_actual remains NULL.
kdump passes the re-IPL parameter block to the dump kernel through os_info. Before commit 3b9678472bab ("s390/ipl: correct kdump reipl block checksum calculation"), the os_info entry was added only for IPL types which initialized reipl_block_actual. That commit moved the os_info update to machine_crash_shutdown(), making it unconditional. As a result, set_os_info_reipl_block() dereferences reipl_block_actual for IPL_TYPE_UNKNOWN. This may happen to work by chance when address zero contains readable lowcore data and the resulting empty os_info entry is ignored by the dump kernel.
Skip the os_info update when no re-IPL parameter block is available. Kdump then collect the dump and reboot without setting re-IPL parameter block.
--- arch/s390/kernel/ipl.c | 2 ++ 1 file changed, 2 insertions(+)
arch/s390/kernel/ipl.c +2 -0
@@ -1156,6 +1156,8 @@ static struct attribute_group reipl_nss_attr_group = {void set_os_info_reipl_block (void ) { +if (!reipl_block_actual) +return ;os_info_entry_add_data (OS_INFO_REIPL_BLOCK , reipl_block_actual, reipl_block_actual->hdr.len); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 545/1102 s390/ipl: Fix NULL deref in dump_reipl without re-IPL parm block
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit 37f61b71cbc0caefc01022a19ee56fc2510e2e6e ]
Unlike kdump, which passes the re-IPL parameter block through os_info, the stand-alone dump passes it through the IPL parm block address and checksum in lowcore.
Some IPL types, like HMC FTP boot or QEMU direct kernel boot, might not provide an IPL parameter block. In this case reipl_type_init() selects IPL_TYPE_UNKNOWN and reipl_block_actual remains NULL. Nevertheless, dump_reipl_run() unconditionally dereferences it when preparing the lowcore fields. This may happen to work by chance when address zero contains readable lowcore data. A zero IPL parameter block address is then stored in lowcore, causing the stand-alone dumper to enter disabled wait after completing the dump.
Explicitly store a zero IPL parameter block address and checksum when no re-IPL parameter block is available. This does not change the behavior:
the stand-alone dumper completes the dump and halts, while valid re-IPL parameter blocks continue to be handled as before.
--- arch/s390/kernel/ipl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
arch/s390/kernel/ipl.c +7 -3
@@ -1910,7 +1910,8 @@ static struct shutdown_action __refdata dump_action = {static void dump_reipl_run (struct shutdown_trigger *trigger) {struct lowcore *abs_lc; -unsigned int csum; +unsigned long ipib =0 ; +unsigned int csum =0 ;/* * Set REIPL_CLEAR flag in os_info flags entry indicating
@@ -1926,9 +1927,12 @@ static void dump_reipl_run(struct shutdown_trigger *trigger) reipl_type == IPL_TYPE_UNKNOWN) os_info_flags |=OS_INFO_FLAG_REIPL_CLEAR ;os_info_entry_add_data (OS_INFO_FLAGS_ENTRY , &os_info_flags,sizeof (os_info_flags)); - csum = (__force unsigned int )cksm (reipl_block_actual, reipl_block_actual->hdr.len,0 ); +if (reipl_block_actual) { + ipib =__pa (reipl_block_actual); + csum = (__force unsigned int )cksm (reipl_block_actual, reipl_block_actual->hdr.len,0 ); + } abs_lc =get_abs_lowcore (); - abs_lc->ipib =__pa (reipl_block_actual); + abs_lc->ipib = ipib; abs_lc->ipib_checksum = csum;put_abs_lowcore (abs_lc);dump_run (trigger);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 546/1102 sched_ext: Fix timer pinning and return value in scx_central
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wanwu Li <[email protected]>
[ Upstream commit 23761359861ca4bb087540937dfea8b0716914c2 ]
central_timerfn() re-arms the timer with a hardcoded BPF_F_TIMER_CPU_PIN flag and ignores the return value, defeating start_central_timer()'s -EINVAL fallback for kernels without the flag (<6.7): on such kernels the first tick kills the timer permanently with no diagnostic. Honor timer_pinned and check the return like the initial arm does.
--- tools/sched_ext/scx_central.bpf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
tools/sched_ext/scx_central.bpf.c +5 -1
@@ -261,6 +261,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)u64 now =bpf_ktime_get_ns ();u64 nr_to_kick = nr_queued;s32 i, curr_cpu; +int ret; curr_cpu =bpf_get_smp_processor_id ();if (timer_pinned && (curr_cpu != central_cpu)) {
@@ -294,7 +295,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)scx_bpf_kick_cpu (cpu,SCX_KICK_PREEMPT ); } -bpf_timer_start (timer,TIMER_INTERVAL_NS ,BPF_F_TIMER_CPU_PIN ); + ret =bpf_timer_start (timer,TIMER_INTERVAL_NS , + timer_pinned ?BPF_F_TIMER_CPU_PIN :0 ); +if (ret) +scx_bpf_error ("bpf_timer_start failed (%d)" , ret);__sync_fetch_and_add (&nr_timers,1 );return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 547/1102 Bluetooth: btintel_pcie: Clear automask on spurious interrupts
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiran K <[email protected]>
[ Upstream commit ea2ee8b222306208d2b094d1a11894da6c106d42 ]
On spurious interrupt where the TX and RX causes are not set, driver was not clearing the auto mask which can block all the interrupts. Driver needs to clear the automask even if no causes are set.
--- drivers/bluetooth/btintel_pcie.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/bluetooth/btintel_pcie.c +3 -0
@@ -838,6 +838,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)if (unlikely (!(intr_fh | intr_hw))) {/* Ignore interrupt, inta == 0 */ +bt_warn_ratelimited ("Bluetooth: btintel_pcie: Received spurious interrupt\n" ); +btintel_pcie_wr_reg32 (data,BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST , +BIT (entry->entry));return IRQ_NONE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 548/1102 workqueue: replace use of system_wq with system_percpu_wq
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marco Crivellari <[email protected]>
[ Upstream commit a2be943b46b4a7478ea8ddf9bb8e5251c59fceb7 ]
Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND.
This lack of consistentcy cannot be addressed without refactoring the API.
system_wq is a per-CPU worqueue, yet nothing in its name tells about that CPU affinity constraint, which is very often not required by users. Make it clear by adding a system_percpu_wq.
queue_work() / queue_delayed_work() mod_delayed_work() will now use the new per-cpu wq: whether the user still stick on the old name a warn will be printed along a wq redirect to the new one.
This patch add the new system_percpu_wq except for mm, fs and net subsystem, whom are handled in separated patches.
The old wq will be kept for a few release cylces.
Stable-dep-of: 068c35b5d054 ("workqueue: reject watchdog thresholds that overflow jiffies")
--- include/linux/workqueue.h | 22 +++++++++++----------- kernel/workqueue.c | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-)
include/linux/workqueue.h +11 -11
@@ -433,10 +433,10 @@ enum wq_consts {* short queue flush time. Don't queue works which can run for too * long. * -* system_highpri_wq is similar to system_wq but for work items which +* system_highpri_wq is similar to system_percpu_wq but for work items which * require WQ_HIGHPRI. * -* system_long_wq is similar to system_wq but may host long running +* system_long_wq is similar to system_percpu_wq but may host long running * works. Queue flushing might take relatively long. * * system_dfl_wq is unbound workqueue. Workers are not bound to
@@ -444,13 +444,13 @@ enum wq_consts {* executed immediately as long as max_active limit is not reached and * resources are available. * -* system_freezable_wq is equivalent to system_wq except that it's +* system_freezable_wq is equivalent to system_percpu_wq except that it's * freezable. * * *_power_efficient_wq are inclined towards saving power and converted * into WQ_UNBOUND variants if 'wq_power_efficient' is enabled; otherwise, * they are same as their non-power-efficient counterparts - e.g. -* system_power_efficient_wq is identical to system_wq if +* system_power_efficient_wq is identical to system_percpu_wq if * 'wq_power_efficient' is disabled. See WQ_POWER_EFFICIENT for more info. * * system_bh[_highpri]_wq are convenience interface to softirq. BH work items
@@ -704,7 +704,7 @@ static inline bool mod_delayed_work(struct workqueue_struct *wq,*/ static inline bool schedule_work_on (int cpu,struct work_struct *work) { -return queue_work_on (cpu, system_wq, work); +return queue_work_on (cpu, system_percpu_wq, work); }/**
@@ -723,7 +723,7 @@ static inline bool schedule_work_on(int cpu, struct work_struct *work)*/ static inline bool schedule_work (struct work_struct *work) { -return queue_work (system_wq, work); +return queue_work (system_percpu_wq, work); }/**
@@ -766,15 +766,15 @@ extern void __warn_flushing_systemwide_wq(void)#define flush_scheduled_work () \ ({ \ __warn_flushing_systemwide_wq(); \ - __flush_workqueue(system_wq); \ + __flush_workqueue(system_percpu_wq); \ })#define flush_workqueue (wq) \ ({ \ struct workqueue_struct *_wq = (wq); \ \ - if ((__builtin_constant_p(_wq == system_wq) && \ - _wq == system_wq) || \ + if ((__builtin_constant_p(_wq == system_percpu_wq) && \ + _wq == system_percpu_wq) || \ (__builtin_constant_p(_wq == system_highpri_wq) && \ _wq == system_highpri_wq) || \ (__builtin_constant_p(_wq == system_long_wq) && \
@@ -803,7 +803,7 @@ extern void __warn_flushing_systemwide_wq(void)static inline bool schedule_delayed_work_on (int cpu,struct delayed_work *dwork,unsigned long delay) { -return queue_delayed_work_on (cpu, system_wq, dwork, delay); +return queue_delayed_work_on (cpu, system_percpu_wq, dwork, delay); }/**
@@ -817,7 +817,7 @@ static inline bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork,static inline bool schedule_delayed_work (struct delayed_work *dwork,unsigned long delay) { -return queue_delayed_work (system_wq, dwork, delay); +return queue_delayed_work (system_percpu_wq, dwork, delay); }#ifndef CONFIG_SMP
kernel/workqueue.c +1 -1
@@ -7705,7 +7705,7 @@ static int wq_watchdog_param_set_thresh(const char *val,if (ret)return ret; -if (system_wq) +if (system_percpu_wq)wq_watchdog_set_thresh (thresh);else wq_watchdog_thresh = thresh;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 549/1102 workqueue: reject watchdog thresholds that overflow jiffies
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiacheng Xu <[email protected]>
[ Upstream commit 068c35b5d0546c8625b3d7c61910f73775cf1216 ]
The watchdog threshold is supplied in seconds but is multiplied by HZ before being used as a jiffies interval. Reject values that exceed MAX_JIFFY_OFFSET / HZ so the multiplication cannot wrap and the time_after() comparisons remain within their supported range.
The check is performed before changing the threshold or watchdog timer. Zero remains the value used to disable the watchdog.
--- kernel/workqueue.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/workqueue.c +3 -0
@@ -7705,6 +7705,9 @@ static int wq_watchdog_param_set_thresh(const char *val,if (ret)return ret; +if (thresh >MAX_JIFFY_OFFSET /HZ ) +return -ERANGE ; +if (system_percpu_wq)wq_watchdog_set_thresh (thresh);else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 550/1102 Bluetooth: btintel: validate version TLV value lengths
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laxman Acharya Padhya <[email protected]>
[ Upstream commit a086c0892969bf8a0151b0f12bd14a68827c88b2 ]
btintel_parse_version_tlv() verifies that a complete TLV is present in the response, but it does not ensure that the value is long enough for the specific TLV type. A short value can therefore cause an out-of-bounds read through get_unaligned_le16(), get_unaligned_le32(), or memcpy().
Reject values shorter than the minimum required by each known TLV type. Also reject responses that do not contain the Command Complete Status field.
--- drivers/bluetooth/btintel.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)
drivers/bluetooth/btintel.c +36 -1
@@ -546,12 +546,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev, }EXPORT_SYMBOL_GPL (btintel_version_info_tlv); +static u8 btintel_version_tlv_min_len (u8 type) +{ +switch (type) { +case INTEL_TLV_CNVI_TOP : +case INTEL_TLV_CNVR_TOP : +case INTEL_TLV_CNVI_BT : +case INTEL_TLV_CNVR_BT : +case INTEL_TLV_BUILD_NUM : +case INTEL_TLV_GIT_SHA1 : +return sizeof (u32); +case INTEL_TLV_DEV_REV_ID : +case INTEL_TLV_TIME_STAMP : +return sizeof (u16); +case INTEL_TLV_IMAGE_TYPE : +case INTEL_TLV_BUILD_TYPE : +case INTEL_TLV_SECURE_BOOT : +case INTEL_TLV_OTP_LOCK : +case INTEL_TLV_API_LOCK : +case INTEL_TLV_DEBUG_LOCK : +case INTEL_TLV_LIMITED_CCE : +case INTEL_TLV_SBE_TYPE : +return sizeof (u8); +case INTEL_TLV_MIN_FW : +return 3 ; +case INTEL_TLV_OTP_BDADDR : +return sizeof (bdaddr_t); +default : +return 0 ; + } +} +int btintel_parse_version_tlv (struct hci_dev *hdev,struct intel_version_tlv *version,struct sk_buff *skb) {/* Consume Command Complete Status field */ -skb_pull (skb,1 ); +if (!skb_pull (skb,1 )) +return -EINVAL ;/* Event parameters contatin multiple TLVs. Read each of them * and only keep the required data. Also, it use existing legacy
@@ -571,6 +603,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,if (skb->len < tlv->len +sizeof (*tlv))return -EINVAL ; +if (tlv->len <btintel_version_tlv_min_len (tlv->type)) +return -EINVAL ; +switch (tlv->type) {case INTEL_TLV_CNVI_TOP : version->cnvi_top =get_unaligned_le32 (tlv->val);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 551/1102 Bluetooth: btintel: bound firmware ID by TLV length
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laxman Acharya Padhya <[email protected]>
[ Upstream commit ac8aa9e0ec93a12a60230066f199f49c3b9aac3d ]
The firmware ID is treated as a NUL-terminated string even though the TLV length is its only boundary. If the value does not contain a NUL terminator, snprintf() can read beyond the received response.
Limit the conversion to the advertised TLV value length.
--- drivers/bluetooth/btintel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel.c +1 -1
@@ -677,7 +677,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,break ;case INTEL_TLV_FW_ID :snprintf (version->fw_id,sizeof (version->fw_id), -"%s" , tlv->val); +"%.*s" , tlv->len, tlv->val);break ;default :/* Ignore rest of information */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 552/1102 Bluetooth: hci_core: Fix race condition during device registration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 57938bbdb9bf7fd41cbd5cd509ec10c4b22bec18 ]
In hci_register_dev(), the power_on work item is queued to hdev->req_workqueue before initializing hdev->adv_monitors_idr and registering the MSFT extension via msft_register(). For devices marked with quirks such as HCI_QUIRK_RAW_DEVICE, the HCI_UNCONFIGURED flag is set on the device. When the power_on work item runs concurrently on another CPU, hci_power_on() detects that the device is unconfigured and immediately invokes hci_dev_do_close(), which calls msft_do_close().
Concurrently, msft_register() allocates the msft structure and exposes it to hdev->msft_data prior to calling mutex_init(&msft->filter_lock). If msft_do_close() executes while hdev->msft_data is already assigned but the mutex has not yet been initialized, mutex_lock(&msft->filter_lock) operates on an uninitialized mutex, triggering a DEBUG_LOCKS warning:
DEBUG_LOCKS_WARN_ON(lock->magic != lock)
WARNING: kernel/locking/mutex.c:625 at __mutex_lock_common kernel/locking/mutex.c:625 [inline] WARNING: kernel/locking/mutex.c:625 at __mutex_lock+0x12d8/0x1550 kernel/locking/mutex.c:821 ... Call Trace: <TASK> msft_do_close+0x308/0x7b0 net/bluetooth/msft.c:693 hci_dev_close_sync+0x86b/0x10a0 net/bluetooth/hci_sync.c:5522 hci_dev_do_close net/bluetooth/hci_core.c:499 [inline] hci_power_on+0x32c/0x750 net/bluetooth/hci_core.c:937 process_one_work kernel/workqueue.c:3322 [inline] process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 worker_thread+0x92d/0xe10 kernel/workqueue.c:3486 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
Fix this by moving the queue_work() call in hci_register_dev() to after idr_init(&hdev->adv_monitors_idr) and msft_register(hdev) so that device structures and extensions are fully initialized before asynchronous tasks can access them. Additionally, assign hdev->msft_data in msft_register() only after mutex_init(&msft->filter_lock) has completed.
--- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
net/bluetooth/hci_core.c +2 -2
@@ -2690,11 +2690,11 @@ int hci_register_dev(struct hci_dev *hdev)if (error)BT_WARN ("register suspend notifier failed error:%d\n" , error); -queue_work (hdev->req_workqueue, &hdev->power_on); -idr_init (&hdev->adv_monitors_idr);msft_register (hdev); +queue_work (hdev->req_workqueue, &hdev->power_on); +return id; err_wqueue:
net/bluetooth/msft.c +1 -1
@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)INIT_LIST_HEAD (&msft->handle_map);INIT_LIST_HEAD (&msft->address_filters); - hdev->msft_data = msft;mutex_init (&msft->filter_lock); + hdev->msft_data = msft; }void msft_release (struct hci_dev *hdev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 553/1102 Bluetooth: L2CAP: fix chan mode for LE_CONN_REQ + EXT_FLOWCTL pchan
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 4ef05db5b08b176a551b4a6287372045998806b0 ]
l2cap_new_connection() sets default value of channel mode to match the parent channel. l2cap_le_connect_req() left this at the default, and created L2CAP_MODE_EXT_FLOWCTL channels if listening pchan has that mode. This causes FLAG_DEFER_SETUP channels to reply to L2CAP_LE_CONN_REQ with L2CAP_ECRED_CONN_RSP, which is incorrect.
It can also result to stack OOB write (of l2cap_alloc_cid determined values) in l2cap_ecred_rsp_defer(), as l2cap_le_connect_req() does not limit maximum number of deferred channels or check for duplicate ident.
Fix by setting chan->mode correctly in l2cap_le_connect_req().
Also check channel mode in l2cap_ecred_rsp_defer(), and do WARN_ON_ONCE instead of OOB write to make it less brittle.
--- net/bluetooth/l2cap_core.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/bluetooth/l2cap_core.c +8 -0
@@ -3858,6 +3858,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)struct l2cap_ecred_conn_rsp *rsp_flex = container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr); +if (chan->mode !=L2CAP_MODE_EXT_FLOWCTL ) +return ; +/* Check if channel for outgoing connection or if it wasn't deferred * since in those cases it must be skipped. */
@@ -3868,6 +3871,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)/* Reset ident so only one response is sent */ chan->ident =0 ; +/* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */ +if (WARN_ON_ONCE (rsp->count >=ARRAY_SIZE (rsp->pdu.scid))) + rsp->pdu.rsp.result =cpu_to_le16 (L2CAP_CR_LE_NO_MEM ); +/* Include all channels pending with the same ident */ if (!rsp->pdu.rsp.result) rsp_flex->dcid[rsp->count++] =cpu_to_le16 (chan->scid);
@@ -5042,6 +5049,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,__set_chan_timer (chan, chan->ops->get_sndtimeo (chan)); chan->ident = cmd->ident; + chan->mode =L2CAP_MODE_LE_FLOWCTL ;if (test_bit (FLAG_DEFER_SETUP , &chan->flags)) {l2cap_state_change (chan,BT_CONNECT2 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 554/1102 Bluetooth: L2CAP: fix out-of-bounds write in l2cap_ecred_connect
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 56c2b5831d39dc84aad2573dc3e197af1a872a05 ]
l2cap_chan_connect() tries to ensure there are no more than L2CAP_ECRED_CONN_SCID_MAX pending ECRED channels, so they fit in the same L2CAP_ECRED_CONN_REQ that l2cap_ecred_connect() constructs.
However, the check only counts deferred channels. If 6 L2CAP sockets are connected at the same time in order DDDDND (D=deferred, N=non-deferred), the last can bump the total to max+1. It results to one __le16 written out of bounds of the scid array, and an invalid ECRED_CONN_REQ being sent.
Fix by leaving room for the non-deferred pending ECRED channels in the counting in l2cap_chan_connect(), so the limit can't be exceeded.
Move counting under same critical section where the channel is added. Although race conditions involving this appear unreachable, it's easier to see.
Also add WARN_ON_ONCE check in l2cap_ecred_defer_connect() to make this less brittle.
--- net/bluetooth/l2cap_core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
net/bluetooth/l2cap_core.c +14 -6
@@ -1340,7 +1340,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)struct l2cap_ecred_conn_data {struct {struct l2cap_ecred_conn_req_hdr req; -__le16 scid[5 ]; +__le16 scid[L2CAP_ECRED_CONN_SCID_MAX ]; } __packed pdu;struct l2cap_chan *chan;struct pid *pid;
@@ -1368,6 +1368,10 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)if (test_and_set_bit (FLAG_ECRED_CONN_REQ_SENT , &chan->flags))return ; +/* Unreachable, checked in l2cap_connect (+timer drops it if reached) */ +if (WARN_ON_ONCE (conn->count >=ARRAY_SIZE (conn->pdu.scid))) +return ; +l2cap_ecred_init (chan,0 );/* Set the same ident so we can match on the rsp */
@@ -7343,6 +7347,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, goto done; } +mutex_lock (&conn->lock); +l2cap_chan_lock (chan); +if (chan->mode ==L2CAP_MODE_EXT_FLOWCTL ) {struct l2cap_chan_data data;
@@ -7350,19 +7357,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, data.pid = chan->ops->get_peer_pid (chan); data.count =1 ; -l2cap_chan_list (conn, l2cap_chan_by_pid, &data); +__l2cap_chan_list (conn, l2cap_chan_by_pid, &data); + +/* Leave room for non-deferred channel that ends the group. */ +if (test_bit (FLAG_DEFER_SETUP , &chan->flags)) + data.count +=1 ;/* Check if there isn't too many channels being connected */ if (data.count >L2CAP_ECRED_CONN_SCID_MAX ) {hci_conn_drop (hcon); err = -EPROTO ; - goto done; + goto chan_unlock; } } -mutex_lock (&conn->lock); -l2cap_chan_lock (chan); -if (cid &&__l2cap_get_chan_by_dcid (conn, cid)) {hci_conn_drop (hcon); err = -EBUSY ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 555/1102 Bluetooth: L2CAP: clear FLAG_DEFER_SETUP only for same PID/PSM
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 0d77683237270702fa93489ca759c89b4e970554 ]
l2cap_ecred_defer_connect() clears FLAG_DEFER_SETUP also for channels with different PID/PSM, which will not be added to the same ECRED_CONN_REQ in any case. Consequently, only one ECRED connection group can work at a time although it appears intended they would be separate for each PID/PSM combination.
Fix by clearing FLAG_DEFER_SETUP only for the connections that could be added in the request. Retain test_bit(FLAG_DEFER_SETUP) before calling get_peer_pid as it may be NULL otherwise.
--- net/bluetooth/l2cap_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/bluetooth/l2cap_core.c +4 -1
@@ -1355,7 +1355,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)if (chan == conn->chan)return ; -if (!test_and_clear_bit (FLAG_DEFER_SETUP , &chan->flags)) +if (!test_bit (FLAG_DEFER_SETUP , &chan->flags))return ; pid = chan->ops->get_peer_pid (chan);
@@ -1365,6 +1365,9 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)return ; +if (!test_and_clear_bit (FLAG_DEFER_SETUP , &chan->flags)) +return ; +if (test_and_set_bit (FLAG_ECRED_CONN_REQ_SENT , &chan->flags))return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 556/1102 Bluetooth: hci_mrvl: Fix wrong return value check of wait_on_bit_timeout()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gongwei Li <[email protected]>
[ Upstream commit 2deb76c21b81e42b3282224f7dd2046fe73fd1e0 ]
wait_on_bit_timeout() returns 0 if the bit was cleared, -EINTR if the process received a signal and the mode permitted wake up on that signal, or -EAGAIN if the timeout elapsed. It never returns 1.
Hence the check "err == 1" in mrvl_load_firmware() is dead code: when the waiting task is interrupted by a signal (-EINTR), the code falls into the "else if (err)" branch and misreports it as "Firmware request timeout" with -ETIMEDOUT instead of propagating -EINTR.
Fix this by testing for -EINTR so that an interrupted firmware load is properly detected and reported.
--- drivers/bluetooth/hci_mrvl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/bluetooth/hci_mrvl.c +1 -2
@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) err =wait_on_bit_timeout (&mrvl->flags,STATE_FW_REQ_PENDING ,TASK_INTERRUPTIBLE ,msecs_to_jiffies (2000 )); -if (err ==1 ) { +if (err == -EINTR ) {bt_dev_err (hdev,"Firmware load interrupted" ); - err = -EINTR ;break ; }else if (err) {bt_dev_err (hdev,"Firmware request timeout" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 557/1102 ASoC: ab8500: Reset the audio block before configuring it
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 8e839bca7793a0b03c005f4b2b0825464290d425 ]
ResetAudn is active low, but the codec probe only deasserts it. It also clears Clk32kOut2Dis despite claiming to disable that output, and writes codec registers before releasing reset.
Pulse ResetAudn before the first audio-bank access and leave the unused 32 kHz output disabled.
--- sound/soc/codecs/ab8500-codec.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
sound/soc/codecs/ab8500-codec.c +13 -13
@@ -1917,18 +1917,18 @@ enum ab8500_filter {static int ab8500_audio_init_audioblock (struct snd_soc_component *component) {int status; +u8 mask =AB8500_STW4500CTRL3_CLK32KOUT2DIS | +AB8500_STW4500CTRL3_RESETAUDN ;dev_dbg (component->dev,"%s: Enter.\n" , __func__); -/* Reset audio-registers and disable 32kHz-clock output 2 */ - status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , -AB8500_STW4500CTRL3_CLK32KOUT2DIS | -AB8500_STW4500CTRL3_RESETAUDN , -AB8500_STW4500CTRL3_RESETAUDN ); +/* Reset the audio registers and disable the unused 32 kHz output. */ + status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, +AB8500_STW4500CTRL3_CLK32KOUT2DIS );if (status <0 )return status; -return 0 ; +return ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, mask); } static int ab8500_audio_setup_mics(struct snd_soc_component *component,
@@ -2461,6 +2461,13 @@ static int ab8500_codec_probe(struct snd_soc_component *component)ab8500_codec_of_probe (dev, np, &codec_pdata); + status =ab8500_audio_init_audioblock (component); +if (status <0 ) { +dev_err (dev,"%s: failed to init audio-block (%d)!\n" , + __func__, status); +return status; + } + status =ab8500_audio_setup_mics (component, &codec_pdata.amics);if (status <0 ) {pr_err ("%s: Failed to setup mics (%d)!\n" , __func__, status);
@@ -2473,13 +2480,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)return status; } - status =ab8500_audio_init_audioblock (component); -if (status <0 ) { -dev_err (dev,"%s: failed to init audio-block (%d)!\n" , - __func__, status); -return status; - } -/* Override HW-defaults */ snd_soc_component_write (component,AB8500_ANACONF5 ,BIT (AB8500_ANACONF5_HSAUTOEN ));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 558/1102 ASoC: ab8500: Repair the DAPM capture graph
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 103fe1a37f040ef6ac9ed1cf33be786149d2bb15 ]
The capture stream routes point away from the stream widget. Digital microphone mux routes are unconditional and bypass their enable bits, and several widgets independently own shared AD path enable bits. The dummy ADC and DAC widgets hide the resulting power graph errors.
Connect each real AIF widget to the stream and main supply, use the mux item names on digital microphone routes, and model shared AD enables as supplies. Also make the ANC DAPM switch writable.
--- sound/soc/codecs/ab8500-codec.c | 120 +++++++++++++++----------------- 1 file changed, 56 insertions(+), 64 deletions(-)
sound/soc/codecs/ab8500-codec.c +56 -64
@@ -303,7 +303,7 @@ static const struct snd_kcontrol_new dapm_anc_in_select[] = {/* ANC - Enable/Disable */ static const struct snd_kcontrol_new dapm_anc_enable[] = {SOC_DAPM_SINGLE ("Switch" ,AB8500_ANCCONF1 , -AB8500_ANCCONF1_ENANC ,0 ,0 ), +AB8500_ANCCONF1_ENANC ,1 ,0 ), };/* ANC to Earpiece - Mute */
@@ -385,12 +385,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {/* DA/AD */ -SND_SOC_DAPM_INPUT ("ADC Input" ), -SND_SOC_DAPM_ADC ("ADC" ,"ab8500_0c" ,SND_SOC_NOPM ,0 ,0 ), - -SND_SOC_DAPM_DAC ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 ), -SND_SOC_DAPM_OUTPUT ("DAC Output" ), -SND_SOC_DAPM_AIF_IN ("DA_IN1" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_AIF_IN ("DA_IN2" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ), SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -582,9 +576,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD3 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD3 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD34 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD34, 0, NULL, 0),/* Mic 2 */
@@ -643,9 +636,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_NOPM, 0, 0, NULL, 0), -SND_SOC_DAPM_MIXER ("AD12 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD12 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD12 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD12, 0, NULL, 0),/* HD Capture path */
@@ -659,12 +651,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD6 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD57 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , -NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD68 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD5768 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD5768, 0, NULL, 0),/* Digital Microphone path */
@@ -696,10 +684,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD4 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD4 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 , -0 ,NULL ,0 ), -/* Acoustical Noise Cancellation path */ SND_SOC_DAPM_INPUT("ANC Configure Input"),
@@ -747,24 +731,17 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"Main Supply", NULL, "Audio Power"}, {"Main Supply", NULL, "Audio Analog Power"}, - {"DAC", NULL, "ab8500_0p"}, - {"DAC", NULL, "Main Supply"}, - {"ADC", NULL, "ab8500_0c"}, - {"ADC", NULL, "Main Supply"}, -/* ANC Configure */ {"ANC Configure Input", NULL, "Main Supply"}, {"ANC Configure Output", NULL, "ANC Configure Input"}, -/* AD/DA */ - {"ADC", NULL, "ADC Input"}, - {"DAC Output", NULL, "DAC"}, -/* Powerup charge pump if DA1/2 is in use */ {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Main Supply"}, {"DA_IN1", NULL, "Charge Pump"}, {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Main Supply"}, {"DA_IN2", NULL, "Charge Pump"}, /* Headset path */
@@ -799,8 +776,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* HF or LineOut path */ {"DA_IN3", NULL, "ab8500_0p"}, + {"DA_IN3", NULL, "Main Supply"}, {"DA3 Channel Volume", NULL, "DA_IN3"}, {"DA_IN4", NULL, "ab8500_0p"}, + {"DA_IN4", NULL, "Main Supply"}, {"DA4 Channel Volume", NULL, "DA_IN4"}, {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
@@ -858,8 +837,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Vibrator path */ {"DA_IN5", NULL, "ab8500_0p"}, + {"DA_IN5", NULL, "Main Supply"}, {"DA5 Channel Volume", NULL, "DA_IN5"}, {"DA_IN6", NULL, "ab8500_0p"}, + {"DA_IN6", NULL, "Main Supply"}, {"DA6 Channel Volume", NULL, "DA_IN6"}, {"VIB1 DAC", NULL, "DA5 Channel Volume"},
@@ -901,13 +882,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD1 Channel Volume", NULL, "AD1 Source Select"}, {"AD2 Channel Volume", NULL, "AD2 Source Select"}, - {"AD12 Enable", NULL, "AD1 Channel Volume"}, - {"AD12 Enable", NULL, "AD2 Channel Volume"}, + {"AD1 Channel Volume", NULL, "AD12 Enable"}, + {"AD2 Channel Volume", NULL, "AD12 Enable"}, - {"AD_OUT1", NULL, "ab8500_0c"}, - {"AD_OUT1", NULL, "AD12 Enable"}, - {"AD_OUT2", NULL, "ab8500_0c"}, - {"AD_OUT2", NULL, "AD12 Enable"}, + {"ab8500_0c", NULL, "AD_OUT1"}, + {"AD_OUT1", NULL, "Main Supply"}, + {"AD_OUT1", NULL, "AD1 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT2"}, + {"AD_OUT2", NULL, "Main Supply"}, + {"AD_OUT2", NULL, "AD2 Channel Volume"}, /* Mic 1 */
@@ -924,11 +907,11 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + {"AD3 Channel Volume", NULL, "AD34 Enable"}, - {"AD3 Enable", NULL, "AD3 Channel Volume"}, - - {"AD_OUT3", NULL, "ab8500_0c"}, - {"AD_OUT3", NULL, "AD3 Enable"}, + {"ab8500_0c", NULL, "AD_OUT3"}, + {"AD_OUT3", NULL, "Main Supply"}, + {"AD_OUT3", NULL, "AD3 Channel Volume"}, /* HD Capture path */
@@ -937,14 +920,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD5 Channel Volume", NULL, "AD5 Source Select"}, {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + {"AD5 Channel Volume", NULL, "AD5768 Enable"}, + {"AD6 Channel Volume", NULL, "AD5768 Enable"}, - {"AD57 Enable", NULL, "AD5 Channel Volume"}, - {"AD68 Enable", NULL, "AD6 Channel Volume"}, - - {"AD_OUT57", NULL, "ab8500_0c"}, - {"AD_OUT57", NULL, "AD57 Enable"}, - {"AD_OUT68", NULL, "ab8500_0c"}, - {"AD_OUT68", NULL, "AD68 Enable"}, + {"ab8500_0c", NULL, "AD_OUT57"}, + {"AD_OUT57", NULL, "Main Supply"}, + {"AD_OUT57", NULL, "AD5 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT68"}, + {"AD_OUT68", NULL, "Main Supply"}, + {"AD_OUT68", NULL, "AD6 Channel Volume"}, /* Digital Microphone path */
@@ -955,17 +939,25 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"DMic 5", NULL, "V-DMIC"}, {"DMic 6", NULL, "V-DMIC"}, - {"AD1 Source Select", NULL, "DMic 1"}, - {"AD2 Source Select", NULL, "DMic 2"}, - {"AD3 Source Select", NULL, "DMic 3"}, - {"AD5 Source Select", NULL, "DMic 5"}, - {"AD6 Source Select", NULL, "DMic 6"}, + {"DMIC1", NULL, "DMic 1"}, + {"DMIC2", NULL, "DMic 2"}, + {"DMIC3", NULL, "DMic 3"}, + {"DMIC4", NULL, "DMic 4"}, + {"DMIC5", NULL, "DMic 5"}, + {"DMIC6", NULL, "DMic 6"}, + + {"AD1 Source Select", "DMic 1", "DMIC1"}, + {"AD2 Source Select", "DMic 2", "DMIC2"}, + {"AD3 Source Select", "DMic 3", "DMIC3"}, + {"AD5 Source Select", "DMic 5", "DMIC5"}, + {"AD6 Source Select", "DMic 6", "DMIC6"}, - {"AD4 Channel Volume", NULL, "DMic 4"}, - {"AD4 Enable", NULL, "AD4 Channel Volume"}, + {"AD4 Channel Volume", NULL, "DMIC4"}, + {"AD4 Channel Volume", NULL, "AD34 Enable"}, - {"AD_OUT4", NULL, "ab8500_0c"}, - {"AD_OUT4", NULL, "AD4 Enable"}, + {"ab8500_0c", NULL, "AD_OUT4"}, + {"AD_OUT4", NULL, "Main Supply"}, + {"AD_OUT4", NULL, "AD4 Channel Volume"}, /* LineIn Bypass path */
@@ -990,13 +982,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Sidetone Filter path */ - {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, - {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "LineIn Left", "AD1 Channel Volume"}, + {"Sidetone Left Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Left Source", "Mic 1", "AD3 Channel Volume"}, {"Sidetone Left Source", "Headset Left", "DA_IN1"}, - {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, - {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"}, + {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"}, {"Sidetone Right Source", "Headset Right", "DA_IN2"}, {"STFIR1 Control", NULL, "Sidetone Left Source"},
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 560/1102 ASoC: ab8500: Validate and program TDM slots correctly
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 85cef7e2004ef5c1a715feaddb55d3f3d27bac0a ]
The interface clock ratio is selected from the slot count alone, ffs() and fls() produce one-based hardware slot numbers, eight-channel mode does not program any mappings, and all register errors are ignored. Invalid masks can also leave a partially programmed interface.
Validate the complete configuration first, derive the supported BCLK ratio from slots times slot width, use zero-based slot indices, program deterministic eight-channel maps, and propagate register failures.
--- sound/soc/codecs/ab8500-codec.c | 216 ++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 93 deletions(-)
sound/soc/codecs/ab8500-codec.c +123 -93
@@ -2159,23 +2159,27 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) {struct snd_soc_component *component = dai->component; -unsigned int val, mask, slot, slots_active; +unsigned int active_mask, clock_ratio, slot, value, ad_out, reg; +unsigned int tx_active, rx_active; +unsigned int conf1_val, conf2_val; +unsigned int mask; +int channel, ret; mask =BIT (AB8500_DIGIFCONF2_IF0WL0 ) |BIT (AB8500_DIGIFCONF2_IF0WL1 ); - val =0 ; + conf2_val =0 ;switch (slot_width) {case 16 :break ;case 20 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL0 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;case 24 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 );break ;case 32 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) | + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) |BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;default :
@@ -2184,27 +2188,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,return -EINVAL ; } -dev_dbg (dai->component->dev,"%s: IF0 slot-width: %d bits.\n" , - __func__, slot_width); -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); - -/* Setup TDM clocking according to slot count */ -dev_dbg (dai->component->dev,"%s: Slots, total: %d\n" , __func__, slots); - mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );switch (slots) {case 2 : - val =AB8500_MASK_NONE ; -break ;case 4 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ); -break ;case 8 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); -break ;case 16 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : dev_err(dai->component->dev,
@@ -2212,92 +2200,134 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, __func__, slots);return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , mask, val); - -/* Setup TDM DA according to active tx slots */ -if (tx_mask & ~0xff ) -return -EINVAL ; - - mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; - tx_mask = tx_mask <<AB8500_DA_DATA0_OFFSET ; - slots_active =hweight32 (tx_mask); - -dev_dbg (dai->component->dev,"%s: Slots, active, TX: %d\n" , __func__, - slots_active); - -switch (slots_active) { -case 0 : + clock_ratio = slots * slot_width; +switch (clock_ratio) { +case 32 : + conf1_val =0 ;break ; -case 1 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 64 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 );break ; -case 2 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); - slot =fls (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 128 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode DA-from-slot mapping is set manually." , - __func__); +case 256 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : -dev_err (dai->component->dev, -"%s: Unsupported number of active TX-slots (%d)!\n" , - __func__, slots_active); +dev_err (component->dev,"%s: Unsupported BCLK ratio (%u)!\n" , + __func__, clock_ratio);return -EINVAL ; } -/* Setup TDM AD according to active RX-slots */ + active_mask =GENMASK (min (slots,8 ) -1 ,0 ); +if ((tx_mask | rx_mask) & ~active_mask) { +dev_err (component->dev,"%s: Slot mask exceeds slot count\n" , + __func__); +return -EINVAL ; + } -if (rx_mask & ~0xff ) + tx_active =hweight32 (tx_mask); + rx_active =hweight32 (rx_mask); +if (tx_active !=0 && tx_active !=1 && tx_active !=2 && + tx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active TX slots (%u)!\n" , + __func__, tx_active); +return -EINVAL ; + } +if (rx_active !=0 && rx_active !=1 && rx_active !=2 && + rx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active RX slots (%u)!\n" , + __func__, rx_active);return -EINVAL ; + } + +dev_dbg (component->dev, +"%s: %d slots of %d bits, TX active: %u, RX active: %u\n" , + __func__, slots, slot_width, tx_active, rx_active); - rx_mask = rx_mask <<AB8500_AD_DATA0_OFFSET ; - slots_active =hweight32 (rx_mask); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , + mask, conf2_val); +if (ret <0 ) +return ret; -dev_dbg (dai->component->dev,"%s: Slots, active, RX: %d\n" , __func__, - slots_active); + mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , + mask, conf1_val); +if (ret <0 ) +return ret; -switch (slots_active) { -case 0 : -break ; -case 1 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component,AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); -break ; -case 2 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); - slot =fls (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , slot)); -break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode AD-to-slot mapping is set manually." , - __func__); -break ; -default : -dev_err (dai->component->dev, -"%s: Unsupported number of active RX-slots (%d)!\n" , - __func__, slots_active); -return -EINVAL ; + mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; +if (tx_active ==1 || tx_active ==2 ) { + slot =__ffs (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF1 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF3 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + +if (tx_active ==2 ) + slot =__fls (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF2 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF4 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + }else if (tx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(tx_mask &BIT (slot))) +continue ; + reg =AB8500_DASLOTCONF1 + channel++; + value = slot +AB8500_DA_DATA0_OFFSET ; + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + } + +if (rx_active ==1 || rx_active ==2 ) { + slot =__ffs (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + +if (rx_active ==2 ) { + slot =__fls (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + }else if (rx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(rx_mask &BIT (slot))) +continue ; + ad_out =AB8500_AD_OUT1 + channel++; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (ad_out, slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 561/1102 net: ethernet: oa_tc6: Interrupt is active low, level triggered.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit b542d13fab0f556660ce5f10179fb3ae66bdb9a4 ]
According OPEN Alliance 10BASET1x MAC-PHY Serial Interface specification, interrupt is active low, level triggered.
Code used edge triggered interrupt which has the risk of losing an interrupt on instances like when interrupt is disabled. Level triggered interrupt won't be deasserted unless handler runs and clear the interrupting conditions.
Interrupt handler mechanism is changed to threaded irq from interrupt handler and kernel thread waiting on work queue. Threaded irq mechanism is best suited for level triggered interrupt as it disables the interrupt until handler is run in thread level, while giving us an ability to have interrupt context handler to signal the threaded irq handler.
Introduced a logic to disable the device interrupt on error. Error could be due in data chunk's header and footer or SPI interface itself. This will avoid having repeated interrupts, in case the driver couldn't recover from the error condition with the available recovery mechanism.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 126 ++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 50 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -50
@@ -7,6 +7,7 @@#include <linux/bitfield.h> #include <linux/iopoll.h> +#include <linux/interrupt.h> #include <linux/mdio.h> #include <linux/phy.h> #include <linux/oa_tc6.h>
@@ -44,6 +45,8 @@#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4)#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3)#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) +#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ + GENMASK(12, 7))/* PHY Clause 22 registers base address and mask */ #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
@@ -121,14 +124,13 @@ struct oa_tc6 {struct sk_buff *ongoing_tx_skb;struct sk_buff *waiting_tx_skb;struct sk_buff *rx_skb; -struct task_struct *spi_thread; -wait_queue_head_t spi_wq;u16 tx_skb_offset;u16 spi_data_tx_buf_offset;u16 tx_credits;u8 rx_chunks_available;bool rx_buf_overflow;bool int_flag; +bool disable_traffic; };enum oa_tc6_header_type {
@@ -669,6 +671,38 @@ static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) } } +static void oa_tc6_cleanup_waiting_tx_skb (struct oa_tc6 *tc6) +{ +if (tc6->waiting_tx_skb) { + tc6->netdev->stats.tx_dropped++; +kfree_skb (tc6->waiting_tx_skb); + tc6->waiting_tx_skb =NULL ; + } +} + +static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +{ +oa_tc6_cleanup_ongoing_tx_skb (tc6); +oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_cleanup_waiting_tx_skb (tc6); +} + +/* If the failure is at SPI interface level, masking and clearing +* the interrupt of the device won't work. Since SPI interrupt is +* disabled, it should stop the repeated interrupts. +*/ +static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) +{ +u32 regval =INT_MASK0_ALL_INTERRUPTS ; + + tc6->disable_traffic = true; +oa_tc6_free_pending_skbs (tc6); +oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval); +oa_tc6_read_register (tc6,OA_TC6_REG_STATUS0 , ®val); +oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , regval); +dev_err (&tc6->spi->dev,"Device interrupt disabled to avoid interrupt storm" ); +} +static int oa_tc6_process_extended_status (struct oa_tc6 *tc6) {u32 value;
@@ -1116,29 +1150,29 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)return 0 ; } -static int oa_tc6_spi_thread_handler (void *data) +static irqreturn_t oa_tc6_macphy_threaded_irq (int irq,void *data) {struct oa_tc6 *tc6 = data; -int ret; - -while (likely (!kthread_should_stop ())) { -/* This kthread will be waken up if there is a tx skb or mac-phy -* interrupt to perform spi transfer with tx chunks. -*/ -wait_event_interruptible (tc6->spi_wq, tc6->int_flag || - (tc6->waiting_tx_skb && - tc6->tx_credits) || -kthread_should_stop ()); - -if (kthread_should_stop ()) -break ; +int ret =0 ; - ret =oa_tc6_try_spi_transfer (tc6); -if (ret) -return ret; +/* It is possible that interrupt woke the thread before it is +* disabled. Until we come up with good recovery mechanism, +* no need to attempt spi transfer, once it fails. Pending skbs +* are already freed. +*/ +if (!tc6->disable_traffic) { +while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret =oa_tc6_try_spi_transfer (tc6); +if (ret) { +disable_irq_nosync (tc6->spi->irq); +oa_tc6_disable_traffic (tc6); +break ; + } + } } -return 0 ; +return IRQ_HANDLED ; }static int oa_tc6_update_buffer_status_from_register (struct oa_tc6 *tc6)
@@ -1172,11 +1206,15 @@ static irqreturn_t oa_tc6_macphy_isr(int irq, void *data)* the previous rx footer. * - extended status event not reported in the previous rx footer. */ - tc6->int_flag = true; -/* Wake spi kthread to perform spi transfer */ -wake_up_interruptible (&tc6->spi_wq); - -return IRQ_HANDLED ; +if (tc6->disable_traffic) +disable_irq_nosync (tc6->spi->irq); +else + tc6->int_flag = true; +/* Wake IRQ thread to perform spi transfer . In case +* disable_traffic is set, threaded irq may run again +* one more time. +*/ +return IRQ_WAKE_THREAD ; }/**
@@ -1213,7 +1251,7 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);*/ netdev_tx_t oa_tc6_start_xmit (struct oa_tc6 *tc6,struct sk_buff *skb) { -if (tc6->waiting_tx_skb) { +if (tc6->disable_traffic || tc6->waiting_tx_skb) {netif_stop_queue (tc6->netdev);return NETDEV_TX_BUSY ; }
@@ -1228,8 +1266,8 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) tc6->waiting_tx_skb = skb;spin_unlock_bh (&tc6->tx_skb_lock); -/* Wake spi kthread to perform spi transfer */ -wake_up_interruptible (&tc6->spi_wq); +/* Wake the threaded IRQ to perform spi transfer. */ +irq_wake_thread (tc6->spi->irq, tc6);return NETDEV_TX_OK ; }
@@ -1322,24 +1360,15 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) goto phy_exit; } -init_waitqueue_head (&tc6->spi_wq); - - tc6->spi_thread =kthread_run (oa_tc6_spi_thread_handler, tc6, -"oa-tc6-spi-thread" ); -if (IS_ERR (tc6->spi_thread)) { -dev_err (&tc6->spi->dev,"Failed to create SPI thread\n" ); - goto phy_exit; - } - -sched_set_fifo (tc6->spi_thread); - - ret =devm_request_irq (&tc6->spi->dev, tc6->spi->irq, oa_tc6_macphy_isr, -IRQF_TRIGGER_FALLING ,dev_name (&tc6->spi->dev), - tc6); + ret =devm_request_threaded_irq (&tc6->spi->dev, tc6->spi->irq, + oa_tc6_macphy_isr, + oa_tc6_macphy_threaded_irq, +IRQF_TRIGGER_LOW |IRQF_ONESHOT , +dev_name (&tc6->spi->dev), tc6);if (ret) {dev_err (&tc6->spi->dev,"Failed to request macphy isr %d\n" , ret); - goto kthread_stop; + goto phy_exit; }/* oa_tc6_sw_reset_macphy() function resets and clears the MAC-PHY reset
@@ -1349,12 +1378,10 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)* 7.7 and 9.2.8.8 in the OPEN Alliance specification for more details. */ tc6->int_flag = true; -wake_up_interruptible (&tc6->spi_wq); +irq_wake_thread (tc6->spi->irq, tc6);return tc6; -kthread_stop: -kthread_stop (tc6->spi_thread); phy_exit:oa_tc6_phy_exit (tc6);return NULL ;
@@ -1367,11 +1394,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);*/ void oa_tc6_exit (struct oa_tc6 *tc6) { + tc6->disable_traffic = true; +disable_irq (tc6->spi->irq);oa_tc6_phy_exit (tc6); -kthread_stop (tc6->spi_thread); -dev_kfree_skb_any (tc6->ongoing_tx_skb); -dev_kfree_skb_any (tc6->waiting_tx_skb); -dev_kfree_skb_any (tc6->rx_skb); +oa_tc6_free_pending_skbs (tc6); }EXPORT_SYMBOL_GPL (oa_tc6_exit);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 563/1102 net: ethernet: oa_tc6: Export standard defined registers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 9210d402bdf54240b8aec9815b2f9aad360fcb42 ]
Move defines for standard Open Alliance TC6 register addresses and subfields in the oa_tc6's header. As such, other ethernet drivers that rely on oa_tc6 can use them directly.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 50 ----------------------------------- include/linux/oa_tc6.h | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 50 deletions(-)
drivers/net/ethernet/oa_tc6.c +0 -50
@@ -12,47 +12,6 @@#include <linux/phy.h> #include <linux/oa_tc6.h> -/* OPEN Alliance TC6 registers */ -/* Standard Capabilities Register */ -#define OA_TC6_REG_STDCAP 0x0002 -#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) - -/* Reset Control and Status Register */ -#define OA_TC6_REG_RESET 0x0003 -#define RESET_SWRESET BIT(0)/* Software Reset */ - -/* Configuration Register #0 */ -#define OA_TC6_REG_CONFIG0 0x0004 -#define CONFIG0_SYNC BIT(15) -#define CONFIG0_ZARFE_ENABLE BIT(12) -#define CONFIG0_PROTE BIT(5) - -/* Status Register #0 */ -#define OA_TC6_REG_STATUS0 0x0008 -#define STATUS0_RESETC BIT(6)/* Reset Complete */ -#define STATUS0_HEADER_ERROR BIT(5) -#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) -#define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) -#define STATUS0_TX_PROTOCOL_ERROR BIT(0) - -/* Buffer Status Register */ -#define OA_TC6_REG_BUFFER_STATUS 0x000B -#define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) -#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) - -/* Interrupt Mask Register #0 */ -#define OA_TC6_REG_INT_MASK0 0x000C -#define INT_MASK0_HEADER_ERR_MASK BIT(5) -#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) -#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) -#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) -#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ - GENMASK(12, 7)) - -/* PHY Clause 22 registers base address and mask */ -#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00 -#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F -/* Control command header */ #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
@@ -82,15 +41,6 @@#define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8)#define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1) -/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the -* OPEN Alliance specification. -*/ -#define OA_TC6_PHY_C45_PCS_MMS2 2/* MMD 3 */ -#define OA_TC6_PHY_C45_PMA_PMD_MMS3 3/* MMD 1 */ -#define OA_TC6_PHY_C45_VS_PLCA_MMS4 4/* MMD 31 */ -#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5/* MMD 7 */ -#define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6/* MMD 13 */ -#define OA_TC6_CTRL_PROT_REPLY_SIZE 4#define OA_TC6_CTRL_HEADER_SIZE 4#define OA_TC6_CTRL_REG_VALUE_SIZE 4
include/linux/oa_tc6.h +50 -0
56 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 565/1102 net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 5443d9c4f55d46634b95432e1e8a40b824019bbb ]
Threaded IRQ uses waiting_tx_skb. Transmit path also uses this pointer without any mutual exclusion protection. As a result, it might leak skb buffer, particularly if threaded IRQ sets disable_traffic true after start_xmit already checked and found that disable_traffic being false, if they happen to run on different cores.
On fatal error, where disable_traffic is set, transmit function drops the packet and return NETDEV_TX_OK. Due to this change, skb_linearize call is moved up to the beginning of the transmit function.
Since skb buffer may be freed from different contexts, dev_kfree_skb_any is used to free skb buffer now, replacing one of the kfree_skb call.
oa_tc6_exit disables the irq before setting disable_traffic true.
Stable-dep-of: 172c974113bf ("net: ethernet: oa_tc6: Improve the error recovery")
--- drivers/net/ethernet/oa_tc6.c | 109 ++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 33 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -33
@@ -640,6 +640,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , value); } +/* Called when a frame that is meant to be transmitted, is dropped. */ +static void oa_tc6_drop_tx_skb (struct oa_tc6 *tc6,struct sk_buff *skb) +{ +if (skb) { + tc6->netdev->stats.tx_dropped++; +dev_kfree_skb_any (skb); + } +} + +static struct sk_buff *oa_tc6_detach_waiting_tx_skb (struct oa_tc6 *tc6) +{ +struct sk_buff *skb; + +lockdep_assert_held (&tc6->tx_skb_lock); + skb = tc6->waiting_tx_skb; + tc6->waiting_tx_skb =NULL ; + +return skb; +} +static void oa_tc6_cleanup_ongoing_rx_skb (struct oa_tc6 *tc6) {if (tc6->rx_skb) {
@@ -651,26 +671,30 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)static void oa_tc6_cleanup_ongoing_tx_skb (struct oa_tc6 *tc6) { -if (tc6->ongoing_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->ongoing_tx_skb); - tc6->ongoing_tx_skb =NULL ; - } +oa_tc6_drop_tx_skb (tc6, tc6->ongoing_tx_skb); + tc6->ongoing_tx_skb =NULL ; }static void oa_tc6_cleanup_waiting_tx_skb (struct oa_tc6 *tc6) { -if (tc6->waiting_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->waiting_tx_skb); - tc6->waiting_tx_skb =NULL ; - } +struct sk_buff *skb; + +spin_lock_bh (&tc6->tx_skb_lock); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); } -static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +static void oa_tc6_free_ongoing_skbs (struct oa_tc6 *tc6) {oa_tc6_cleanup_ongoing_tx_skb (tc6);oa_tc6_cleanup_ongoing_rx_skb (tc6); +} + +static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +{ +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_cleanup_waiting_tx_skb (tc6); }
@@ -681,9 +705,15 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) {u32 regval =OA_TC6_INT_MASK0_ALL_INTERRUPTS ; +struct sk_buff *skb; +spin_lock_bh (&tc6->tx_skb_lock); tc6->disable_traffic = true; -oa_tc6_free_pending_skbs (tc6); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);oa_tc6_read_register (tc6,OA_TC6_REG_STATUS0 , ®val);oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , regval);
@@ -1124,8 +1154,7 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)if (ret == -EAGAIN )continue ; -oa_tc6_cleanup_ongoing_tx_skb (tc6); -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret; }
@@ -1147,15 +1176,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)* no need to attempt spi transfer, once it fails. Pending skbs * are already freed. */ -if (!tc6->disable_traffic) { -while (tc6->int_flag || - (tc6->waiting_tx_skb && tc6->tx_credits)) { - ret =oa_tc6_try_spi_transfer (tc6); -if (ret) { -disable_irq_nosync (tc6->spi->irq); -oa_tc6_disable_traffic (tc6); -break ; - } +spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +return IRQ_HANDLED ; + } +spin_unlock_bh (&tc6->tx_skb_lock); + +while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret =oa_tc6_try_spi_transfer (tc6); +if (ret) { +disable_irq_nosync (tc6->spi->irq); +oa_tc6_disable_traffic (tc6); +break ; } }
@@ -1234,23 +1268,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);* @tc6: oa_tc6 struct. * @skb: socket buffer in which the ethernet frame is stored. * -* Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q -* otherwise returns NETDEV_TX_BUSY. +* Return: NETDEV_TX_OK either on successful queueing of the packet for +* transmission, or on packet getting dropped. Packet can be dropped due to +* failure in linearizing the buffer or disable_traffic is set due to +* earlier fatal error. Returns NETDEV_TX_BUSY when there is no room +* to queue the packet. */ netdev_tx_t oa_tc6_start_xmit (struct oa_tc6 *tc6,struct sk_buff *skb) { -if (tc6->disable_traffic || tc6->waiting_tx_skb) { -netif_stop_queue (tc6->netdev); -return NETDEV_TX_BUSY ; - } -if (skb_linearize (skb)) { -dev_kfree_skb_any (skb); - tc6->netdev->stats.tx_dropped++; +oa_tc6_drop_tx_skb (tc6, skb);return NETDEV_TX_OK ; }spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->waiting_tx_skb) { +netif_stop_queue (tc6->netdev); +spin_unlock_bh (&tc6->tx_skb_lock); +return NETDEV_TX_BUSY ; + } +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +oa_tc6_drop_tx_skb (tc6, skb); +return NETDEV_TX_OK ; + } tc6->waiting_tx_skb = skb;spin_unlock_bh (&tc6->tx_skb_lock);
@@ -1404,8 +1445,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);*/ void oa_tc6_exit (struct oa_tc6 *tc6) { - tc6->disable_traffic = true;disable_irq (tc6->spi->irq); +spin_lock_bh (&tc6->tx_skb_lock); + tc6->disable_traffic = true; +spin_unlock_bh (&tc6->tx_skb_lock);oa_tc6_phy_exit (tc6);oa_tc6_free_pending_skbs (tc6); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 566/1102 net: ethernet: oa_tc6: Improve the error recovery
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 172c974113bffe5723b80b1acac17593bb50513c ]
When oversubscribed traffic causes lot of buffer overflow errors, probably due to loss of data chunks, driver fails to find a data chunk with end_valid bit set, before it runs out of sk buffer space. As a result, assert is seen during skb_put.
Now, check is made if skb buffer has enough tailroom for the incoming data before accepting. If there is no room, current frame is abandoned and it will start looking for a data chunk with start_valid bit, that is a new frame.
SK buffer allocation error is considered as recoverable error.
rx_buf_overflow flag is too specific and no longer the only condition this flag is used for. Therefore it is renamed as wait_until_start_valid. This is more appropriate as this flag is used to look for the next data chunk with SV bit set, after failures like buffer overflow, buffer allocation failure, skb pointer validity besides buffer overflow error.
Not writing to status0 if it reads 0.
--- drivers/net/ethernet/oa_tc6.c | 143 +++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 35 deletions(-)
drivers/net/ethernet/oa_tc6.c +108 -35
@@ -82,7 +82,7 @@ struct oa_tc6 {u16 spi_data_tx_buf_offset;u16 tx_credits;u8 rx_chunks_available; -bool rx_buf_overflow; +bool wait_until_start_valid;bool int_flag;bool disable_traffic;bool prot_ctrl;
@@ -698,6 +698,12 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)oa_tc6_cleanup_waiting_tx_skb (tc6); } +static void oa_tc6_look_for_new_frame (struct oa_tc6 *tc6) +{ + tc6->wait_until_start_valid = true; +oa_tc6_cleanup_ongoing_rx_skb (tc6); +} +/* If the failure is at SPI interface level, masking and clearing * the interrupt of the device won't work. Since SPI interrupt is * disabled, it should stop the repeated interrupts.
@@ -732,6 +738,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)return ret; } +/* This function is called for each chunk received in a given SPI +* transaction. In case, extended status bit is set in more than +* one chunk, skip the write, if status0 is already cleared. +*/ +if (!value) +return 0 ; +/* Clear the error interrupts status */ ret =oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , value);if (ret) {
@@ -741,8 +754,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) }if (FIELD_GET (OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { - tc6->rx_buf_overflow = true; -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_look_for_new_frame (tc6);net_err_ratelimited ("%s: Receive buffer overflow error\n" , tc6->netdev->name);return -EAGAIN ;
@@ -768,6 +780,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)static int oa_tc6_process_rx_chunk_footer (struct oa_tc6 *tc6,u32 footer) { +int ret =0 ; +/* Process rx chunk footer for the following, * 1. tx credits * 2. errors if any from MAC-PHY
@@ -778,9 +792,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) footer);if (FIELD_GET (OA_TC6_DATA_FOOTER_EXTENDED_STS , footer)) { -int ret =oa_tc6_process_extended_status (tc6); - -if (ret) + ret =oa_tc6_process_extended_status (tc6); +/* EAGAIN error is recoverable. Move on to check +* HEADER and SYNC errors before returning. +*/ +if (ret && ret != -EAGAIN )return ret; }
@@ -798,7 +814,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)return -ENODEV ; } -return 0 ; +return ret; }static void oa_tc6_submit_rx_skb (struct oa_tc6 *tc6)
@@ -823,13 +839,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) tc6->rx_skb =NULL ; } -static void oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) +/* On oversubscribed traffic condition, particularly with overwhelming rx +* buffer overflow errors, there could be data chunk loss. If tail + length +* goes beyond end pointer, that is an indication that the data chunk with +* end_valid bit is lost. Time to look for a data chunk with start_valid bit. +* +* If rx_skb is NULL, it is time to start looking for data chunk with +* start_bit. +*/ +static int oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) { +if (!tc6->rx_skb || +skb_tailroom (tc6->rx_skb) < length) { +oa_tc6_look_for_new_frame (tc6); +return -EAGAIN ; + } +memcpy (skb_put (tc6->rx_skb, length), payload, length); +return 0 ; } +/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is +* possible that we get two data chunks with start_valid bit set, without +* end_valid bit set in between. In this case, rx_skb would have a valid +* buffer pointer. We should release, if a valid pointer is found before +* allocating a new one. +*/ static int oa_tc6_allocate_rx_skb (struct oa_tc6 *tc6) { +oa_tc6_cleanup_ongoing_rx_skb (tc6); tc6->rx_skb =netdev_alloc_skb_ip_align (tc6->netdev, tc6->netdev->mtu +ETH_HLEN +ETH_FCS_LEN );if (!tc6->rx_skb) {
@@ -849,7 +887,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (ret) +return ret;oa_tc6_submit_rx_skb (tc6);
@@ -864,22 +904,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); - -return 0 ; +return oa_tc6_update_rx_skb (tc6, payload, size); } -static void oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) +static int oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) { -oa_tc6_update_rx_skb (tc6, payload, size); +int ret; -oa_tc6_submit_rx_skb (tc6); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (!ret) +oa_tc6_submit_rx_skb (tc6); +return ret; } -static void oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, -u32 footer) +static int oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, +u32 footer) { -oa_tc6_update_rx_skb (tc6, payload,OA_TC6_CHUNK_PAYLOAD_SIZE ); +return oa_tc6_update_rx_skb (tc6, payload, +OA_TC6_CHUNK_PAYLOAD_SIZE ); } static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
@@ -894,10 +936,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,u16 size;/* Restart the new rx frame after receiving rx buffer overflow error */ -if (start_valid && tc6->rx_buf_overflow) - tc6->rx_buf_overflow = false; +if (start_valid && tc6->wait_until_start_valid) + tc6->wait_until_start_valid = false; -if (tc6->rx_buf_overflow) +if (tc6->wait_until_start_valid)return 0 ;/* Process the chunk with complete rx frame */
@@ -919,8 +961,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,/* Process the chunk with only rx frame end */ if (end_valid && !start_valid) { size = end_byte_offset +1 ; -oa_tc6_prcs_rx_frame_end (tc6, data, size); -return 0 ; +return oa_tc6_prcs_rx_frame_end (tc6, data, size); }/* Process the chunk with previous rx frame end and next rx frame
@@ -934,6 +975,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,if (tc6->rx_skb) { size = end_byte_offset +1 ;oa_tc6_prcs_rx_frame_end (tc6, data, size); + +/* Return value from oa_tc6_prcs_rx_frame_end is not +* checked. If it returned an error, it is to make +* the code to look for new frame. At this stage, +* code below is going to process a new frame. So, +* error condition is set to false, in case it is +* set before proceeding. +*/ + tc6->wait_until_start_valid = false; } size =OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6,
@@ -942,9 +992,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, }/* Process the chunk with ongoing rx frame data */ -oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); - -return 0 ; +return oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); }static u32 oa_tc6_get_rx_chunk_footer (struct oa_tc6 *tc6,u16 footer_offset)
@@ -960,8 +1008,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)static int oa_tc6_process_spi_data_rx_buf (struct oa_tc6 *tc6,u16 length) {u16 no_of_rx_chunks = length /OA_TC6_CHUNK_SIZE ; +bool retry = false; +int ret =0 ;u32 footer; -int ret;/* All the rx chunks in the receive SPI data buffer are examined here */ for (int i =0 ; i < no_of_rx_chunks; i++) {
@@ -970,8 +1019,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) OA_TC6_CHUNK_PAYLOAD_SIZE); ret =oa_tc6_process_rx_chunk_footer (tc6, footer); -if (ret) -return ret; +if (ret) { +if (ret != -EAGAIN ) +return ret; + retry = true; + }/* If there is a data valid chunks then process it for the * information needed to determine the validity and the location
@@ -983,12 +1035,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) ret =oa_tc6_prcs_rx_chunk_payload (tc6, payload, footer); -if (ret) -return ret; +if (ret) { +if (ret != -ENOMEM && ret != -EAGAIN ) +return ret; + retry = true; + } } } -return 0 ; +/* Not bailing out on recoverable error codes, -EAGAIN and +* -ENOMEM. If subsequent loop iterations, if any, succeeds, +* error code would be overwritten. retry flag helps to +* make the caller to continue and retry. Since recovery +* action for -ENOMEM and -EAGAIN are same, we are returning +* one of the error codes, that is -EAGAIN. +* +* Successful recovery depends on how small the frames are, +* how many chunks, among the received chunks triggered the +* error, whether data is intact even with error conditions. +* As a result, there is no single, best method to recover +* most data when error conditions hit. We do our best by +* processing all the chunks with good "footer header" and +* "data valid" bit set. +*/ +if (retry) { + ret = -EAGAIN ; +oa_tc6_look_for_new_frame (tc6); + } + +return ret; } static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
@@ -1150,10 +1225,8 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) } ret =oa_tc6_process_spi_data_rx_buf (tc6, spi_len); -if (ret) { -if (ret == -EAGAIN ) -continue ; +if (ret && ret != -EAGAIN ) {oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 567/1102 net: ethernet: oa_tc6: Disable tx queues on fatal error
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 349c366365876b7f67120827a0deb44899f59303 ]
Previously, TX queue interface was stopped when disable_traffic flag was set, which would indicate fatal error. It is more appropriate to disable the queue as, unless driver is unloaded and reloaded, there is no recovery after disable_traffic is set.
Queues may be re-enabled inadvertently by other layers. Intention of disable_traffic is only to stop the traffic from flowing on fatal error.
--- drivers/net/ethernet/oa_tc6.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/ethernet/oa_tc6.c +4 -0
@@ -718,6 +718,10 @@ static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) skb =oa_tc6_detach_waiting_tx_skb (tc6);spin_unlock_bh (&tc6->tx_skb_lock); +/* disable_traffic, when set, is a point of no return to +* working state. Keeping the TX queues disabled. +*/ +netif_tx_disable (tc6->netdev);oa_tc6_drop_tx_skb (tc6, skb);oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 568/1102 net: ethernet: oa_tc6: Fix for the wrong data type
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 3cc2aa96b97184abd6fc106aac626ddf14389813 ]
Inadvertently bool data type is used where int is supposed to be used. This might turn a negative error code into true or false and sign of the return code would be lost.
--- drivers/net/ethernet/oa_tc6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/oa_tc6.c +1 -1
@@ -411,7 +411,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum) {struct oa_tc6 *tc6 = bus->priv;u32 regval; -bool ret; +int ret; ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 569/1102 net/sched: cls_u32: fix duplicate handle when node ID pool is exhausted
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit d7e7e98d23f42a92d9ab7e36302bd96bd9b33b5f ]
gen_new_kid() falls back to returning max (htid | 0xFFF) when both idr_alloc_u32() ranges are full, instead of reporting an error. u32_change() trusts that value and inserts a new knode with a handle that is already live in the hash table, breaking handle uniqueness within the table's node ID space.
The handle was never reserved in ht->handle_idr, so every later error path that does idr_remove(&ht->handle_idr, handle) removes the reservation of a different, live knode, which is then reused — one failed add compounds into further duplicates.
The 4095 limit is per (table, bucket) — ht->handle_idr is per hash table and the range is derived from htid (bucketid), so a table with divisor 256 can legitimately hold 256*4095 knodes.
The sibling helper gen_new_htid() has the same silent in-band failure:
it returns 0 when the tp_c handle pool (1..0x7FF) is full, and u32_init() publishes the root hash table with handle 0 without checking. Two root tables with handle 0 alias in u32_lookup_ht(), allowing cross-tcf_proto knode add/lookup/delete. Add the same exhaustion check that the divisor path already has.
Return an error so u32_change() fails with ENOSPC/ENOMEM when the node ID space is exhausted, and so u32_init() fails with -ENOMEM when the hash table ID space is exhausted. The extack message distinguishes pool exhaustion (-ENOSPC) from a transient allocation failure (-ENOMEM).
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_CLS_U32=y (or =m with module loaded)
- Create a clsact qdisc on a device, then add 4095 u32 filters with auto-generated handles to fill the node ID space for the root hash table (single bucket). The 4096th auto-handle filter add triggers the duplicate handle (fh 800::fff reused). Reachable at Level 2 (unshare -Urn, namespace-local CAP_NET_ADMIN).
- For gen_new_htid: create 2047 u32 proto entries on the same block to fill the tp_c handle pool, then create one more. The root table gets handle 0 and aliases with other handle-0 root tables.
--- net/sched/cls_u32.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
net/sched/cls_u32.c +26 -6
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)refcount_set (&root_ht->refcnt,1 ); root_ht->handle = tp_c ?gen_new_htid (tp_c, root_ht) :id2handle (0 ); +if (root_ht->handle ==0 ) { +kfree (root_ht); +return -ENOMEM ; + } root_ht->prio = tp->prio; root_ht->is_root = true;idr_init (&root_ht->handle_idr);
@@ -695,21 +699,33 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,return ret; } -static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid) +static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid,int *err) {u32 index = htid |0x800 ;u32 max = htid |0xFFF ; + *err =0 ; +if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max,GFP_KERNEL )) { index = htid +1 ; -if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, -GFP_KERNEL )) - index = max; + *err =idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, +GFP_KERNEL ); +if (*err) +return 0 ; }return index; } +static int u32_kid_extack (int err,struct netlink_ext_ack *extack) +{ +if (err == -ENOSPC ) +NL_SET_ERR_MSG_MOD (extack,"Hash table node ID pool exhausted" ); +else +NL_SET_ERR_MSG_MOD (extack,"Failed to allocate node ID" ); +return err; +} +static const struct nla_policy u32_policy[TCA_U32_MAX +1 ] = { [TCA_U32_CLASSID ] = { .type =NLA_U32 }, [TCA_U32_HASH ] = { .type =NLA_U32 },
@@ -1076,7 +1092,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,* handle which is used to uniquely identify the match entry. */ if (!TC_U32_NODE (handle)) { - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }else { handle = htid |TC_U32_NODE (handle); err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1088,7 +1106,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,/* The user did not give us a handle; lets just generate one * from the table's pool of nodeids. */ - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }if (tb[TCA_U32_SEL ] ==NULL ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 570/1102 igmp: convert struct ip_sf_list to RCU
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 2987ee196c88dbde0463dc87d5fb209c684e34a2 ]
Commit 23d2b94043ca ("igmp: Add ip_mc_list lock in ip_check_mc_rcu") added spin_lock_bh(&im->lock) to ip_check_mc_rcu() to prevent a use-after-free while iterating im->sources during concurrent deletions.
However, ip_check_mc_rcu() is called from RCU read-side critical sections in packet receive and route lookup fast paths (e.g. __mkroute_output(), ip_route_input_rcu(), and __udp4_lib_rcv()).
When igmpv3_send_cr() or igmpv3_send_report() holds &pmc->lock and calls add_grec() -> igmpv3_newpack() -> ip_route_output_ports(), an XFRM policy matching a multicast destination triggers xfrm_tmpl_resolve_one() -> xfrm4_get_saddr() -> __mkroute_output() -> ip_check_mc_rcu(). This attempts to acquire &im->lock while &pmc->lock is already held on the same CPU, triggering a lockdep recursive locking warning / deadlock.
Fix this by converting IPv4 struct ip_sf_list to RCU, mirroring the IPv6 implementation in net/ipv6/mcast.c:
1. Add struct rcu_head to struct ip_sf_list and annotate sf_next, sources, and tomb as __rcu pointers.
2. Use rcu_assign_pointer() and kfree_rcu() for list updates and deletions.
3. Remove spin_lock_bh(&im->lock) from ip_check_mc_rcu() and traverse im->sources locklessly with for_each_psf_rcu(), reading and writing counter fields with READ_ONCE() and WRITE_ONCE().
Note: RCU conversion of /proc/net/mcfilter will be done in a separate patch.
--- include/linux/igmp.h | 7 +- net/ipv4/igmp.c | 210 +++++++++++++++++++++++++++---------------- 2 files changed, 135 insertions(+), 82 deletions(-)
include/linux/igmp.h +4 -3
@@ -57,20 +57,21 @@ struct ip_mc_socklist { };struct ip_sf_list { -struct ip_sf_list *sf_next; +struct ip_sf_list __rcu *sf_next;unsigned long sf_count[2 ];/* include/exclude counts */ __be32 sf_inaddr;unsigned char sf_gsresp;/* include in g & s response? */ unsigned char sf_oldin;/* change state */ unsigned char sf_crcount;/* retrans. left to send */ +struct rcu_head rcu; };struct ip_mc_list {struct in_device *interface;__be32 multiaddr;unsigned int sfmode; -struct ip_sf_list *sources; -struct ip_sf_list *tomb; +struct ip_sf_list __rcu *sources; +struct ip_sf_list __rcu *tomb;unsigned long sfcount[2 ];union {struct ip_mc_list *next;
net/ipv4/igmp.c +131 -79
466 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 571/1102 ppp: ppp_async: simplify tty disc_data access
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit 9feb069e5ed03582fbf6272539f1caa2a17dc6d5 ]
tty_ldisc_hangup() invokes the hangup callback while holding only a read lock on tty->ldisc_sem, so it can run concurrently with other line discipline callbacks. This currently forces async PPP to maintain separate lifetime protection around tty->disc_data.
Line discipline close is called under the write lock during hangup processing. Remove the hangup callback and rely on close for teardown, as done for SLIP by commit 23c53269f2ba ("slip: remove slip_hangup() to fix use-after-free in slip_receive_buf()"). This serializes teardown with all other line discipline operations.
disc_data_lock, refcount and completion are redundant with that serialization. Remove them and access tty->disc_data directly.
This also eliminates a lockdep warning reported by syzbot. The warning does not indicate a real deadlock because the write side runs only in process context with hardirqs disabled.
--- drivers/net/ppp/ppp_async.c | 82 ++++--------------------------------- 1 file changed, 7 insertions(+), 75 deletions(-)
drivers/net/ppp/ppp_async.c +7 -75
@@ -65,8 +65,6 @@ struct asyncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead;struct ppp_channel chan;/* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE ]; };
@@ -116,38 +114,6 @@ static const struct ppp_channel_ops async_ops = {* Routines implementing the PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_asynctty_receive while another -* calls ppp_asynctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_asynctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: this is no longer true. The _close path for the ldisc is -* now guaranteed to be sane. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct asyncppp *ap_get (struct tty_struct *tty) -{ -struct asyncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void ap_put (struct asyncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead); -} -/* * Called when a tty is put into PPP line discipline. Called in process * context.
@@ -182,9 +148,6 @@ ppp_asynctty_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_async_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead); - ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu =PPP_MRU ;
@@ -205,34 +168,18 @@ ppp_asynctty_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_asynctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the asyncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_asynctty_close (struct tty_struct *tty) { -struct asyncppp *ap; +struct asyncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_async_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -242,17 +189,6 @@ ppp_asynctty_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_asynctty_hangup (struct tty_struct *tty) -{ -ppp_asynctty_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -283,7 +219,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_asynctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;int err, val;int __user *p = (int __user *)arg;
@@ -324,7 +260,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) err =tty_mode_ioctl (tty, cmd, arg); } -ap_put (ap);return err; }
@@ -333,7 +268,7 @@ static void ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -343,21 +278,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -ap_put (ap);tty_unthrottle (tty); }static void ppp_asynctty_wakeup (struct tty_struct *tty) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -ap_put (ap); }
@@ -367,7 +300,6 @@ static struct tty_ldisc_ops ppp_ldisc = { .name = "ppp", .open = ppp_asynctty_open, .close = ppp_asynctty_close, - .hangup = ppp_asynctty_hangup, .read = ppp_asynctty_read, .write = ppp_asynctty_write, .ioctl = ppp_asynctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 572/1102 ppp: ppp_synctty: simplify tty disc_data access
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit d8d4d1cf40d541a5d7cc3b15d57e42d0815c7d53 ]
Apply the same simplification as the preceding ppp_async change.
--- drivers/net/ppp/ppp_synctty.c | 83 +++-------------------------------- 1 file changed, 7 insertions(+), 76 deletions(-)
drivers/net/ppp/ppp_synctty.c +7 -76
@@ -38,11 +38,9 @@#include <linux/ppp-ioctl.h> #include <linux/ppp_channel.h> #include <linux/spinlock.h> -#include <linux/completion.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> -#include <linux/refcount.h> #include <linux/unaligned.h> #include <linux/uaccess.h>
@@ -69,8 +67,6 @@ struct syncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead_cmp;struct ppp_channel chan;/* interface to generic ppp layer */ };
@@ -118,37 +114,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)* Routines implementing the synchronous PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_synctty_receive while another -* calls ppp_synctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_synctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: Fixed in tty_io nowadays. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct syncppp *sp_get (struct tty_struct *tty) -{ -struct syncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void sp_put (struct syncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead_cmp); -} -/* * Called when a tty is put into sync-PPP line discipline. */
@@ -179,9 +144,6 @@ ppp_sync_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_sync_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead_cmp); - ap->chan.private = ap; ap->chan.ops = &sync_ops; ap->chan.mtu =PPP_MRU ;
@@ -203,34 +165,18 @@ ppp_sync_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_synctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the syncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_sync_close (struct tty_struct *tty) { -struct syncppp *ap; +struct syncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_sync_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead_cmp); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -239,17 +185,6 @@ ppp_sync_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_sync_hangup (struct tty_struct *tty) -{ -ppp_sync_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -275,7 +210,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_synctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;int __user *p = (int __user *)arg;int err, val;
@@ -316,7 +251,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)break ; } -sp_put (ap);return err; }
@@ -325,7 +259,7 @@ static void ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -335,21 +269,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -sp_put (ap);tty_unthrottle (tty); }static void ppp_sync_wakeup (struct tty_struct *tty) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -sp_put (ap); }
@@ -359,7 +291,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = { .name = "pppsync", .open = ppp_sync_open, .close = ppp_sync_close, - .hangup = ppp_sync_hangup, .read = ppp_sync_read, .write = ppp_sync_write, .ioctl = ppp_synctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 573/1102 ipv6: mcast: fix RCU list diversion in ip6_mc_del1_src()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 93b49239840b91313adbd77b8b52993eff2d08c1 ]
When removing a source filter whose count reaches zero, ip6_mc_del1_src() unlinks psf from pmc->mca_sources. If the filter was previously active, the code moved psf directly into pmc->mca_tomb by updating psf->sf_next.
Because pmc->mca_sources is traversed locklessly under RCU (e.g. by ipv6_chk_mcast_addr()), mutating psf->sf_next before a grace period elapses diverts concurrent readers to the tombstone list. Consequently, readers miss remaining active sources in pmc->mca_sources and improperly examine deleted tombstone entries.
Fix this by allocating a new tombstone node for pmc->mca_tomb (as done in sf_setstate()) and retiring the original psf via kfree_rcu().
--- net/ipv6/mcast.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
net/ipv6/mcast.c +11 -7
@@ -2331,14 +2331,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,if (psf->sf_oldin && !(pmc->mca_flags &MAF_NOREPORT ) && !mld_in_v1_mode (idev)) { - psf->sf_crcount = idev->mc_qrv; -rcu_assign_pointer (psf->sf_next, -mc_dereference (pmc->mca_tomb, idev)); -rcu_assign_pointer (pmc->mca_tomb, psf); - rv =1 ; - }else { -kfree_rcu (psf, rcu); +struct ip6_sf_list *dpsf =kmalloc_obj (*dpsf); + +if (dpsf) { + *dpsf = *psf; + dpsf->sf_crcount = idev->mc_qrv; +rcu_assign_pointer (dpsf->sf_next, +mc_dereference (pmc->mca_tomb, idev)); +rcu_assign_pointer (pmc->mca_tomb, dpsf); + rv =1 ; + } } +kfree_rcu (psf, rcu); }return rv; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 574/1102 ipv6: mcast: Dont hold RTNL for IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 1767bb2d47b715a106287a8f963d9ec6cbab4e69 ]
In __ipv6_sock_mc_join(), per-socket mld data is protected by lock_sock(), and only __dev_get_by_index() requires RTNL.
Let's use dev_get_by_index() and drop RTNL for IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.
Note that we must call rt6_lookup() and dev_hold() under RCU.
If rt6_lookup() returns an entry from the exception table, dst_dev_put() could change rt->dev.dst to loopback concurrently, and the original device could lose the refcount before dev_hold() and unblock device registration.
dst_dev_put() is called from NETDEV_UNREGISTER and synchronize_net() follows it, so as long as rt6_lookup() and dev_hold() are called within the same RCU critical section, the dev is alive.
Even if the race happens, they are synchronised by idev->dead and mcast addresses are cleaned up.
For the racy access to rt->dst.dev, we use dst_dev().
Stable-dep-of: c073d1b070f1 ("ipv6: mcast: use copy-on-write RCU updates in ip6_mc_source()")
--- net/ipv6/ipv6_sockglue.c | 2 -- net/ipv6/mcast.c | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-)
net/ipv6/ipv6_sockglue.c +0 -2
@@ -121,11 +121,9 @@ static bool setsockopt_needs_rtnl(int optname) {switch (optname) {case IPV6_ADDRFORM : -case IPV6_ADD_MEMBERSHIP :case IPV6_DROP_MEMBERSHIP :case IPV6_JOIN_ANYCAST :case IPV6_LEAVE_ANYCAST : -case MCAST_JOIN_GROUP :case MCAST_LEAVE_GROUP :case MCAST_JOIN_SOURCE_GROUP :case MCAST_LEAVE_SOURCE_GROUP :
net/ipv6/mcast.c +13 -11
@@ -172,14 +172,12 @@ static int unsolicited_report_interval(struct inet6_dev *idev)static int __ipv6_sock_mc_join (struct sock *sk,int ifindex,const struct in6_addr *addr,unsigned int mode) { -struct net_device *dev =NULL ; -struct ipv6_mc_socklist *mc_lst;struct ipv6_pinfo *np =inet6_sk (sk); +struct ipv6_mc_socklist *mc_lst;struct net *net =sock_net (sk); +struct net_device *dev =NULL ;int err; -ASSERT_RTNL (); -if (!ipv6_addr_is_multicast (addr))return -EINVAL ;
@@ -199,13 +197,18 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,if (ifindex ==0 ) {struct rt6_info *rt; + +rcu_read_lock (); rt =rt6_lookup (net, addr,NULL ,0 ,NULL ,0 );if (rt) { - dev = rt->dst.dev; + dev =dst_dev (&rt->dst); +dev_hold (dev);ip6_rt_put (rt); } - }else - dev =__dev_get_by_index (net, ifindex); +rcu_read_unlock (); + }else { + dev =dev_get_by_index (net, ifindex); + }if (!dev) {sock_kfree_s (sk, mc_lst,sizeof (*mc_lst));
@@ -216,12 +219,11 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex, mc_lst->sfmode = mode;RCU_INIT_POINTER (mc_lst->sflist,NULL ); -/* -* now add/increase the group membership on the device -*/ - +/* now add/increase the group membership on the device */ err =__ipv6_dev_mc_inc (dev, addr, mode); +dev_put (dev); +if (err) {sock_kfree_s (sk, mc_lst,sizeof (*mc_lst));return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 575/1102 ipv6: mcast: Dont hold RTNL for IPV6_DROP_MEMBERSHIP and MCAST_LEAVE_GROUP.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 2ceb71ce7d34e751f91bbca9da3513a2bc29089c ]
In __ipv6_sock_mc_drop(), per-socket mld data is protected by lock_sock(), and only __dev_get_by_index() and __in6_dev_get() require RTNL.
Let's use dev_get_by_index() and in6_dev_get() and drop RTNL for IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.
Note that __ipv6_sock_mc_drop() is factorised to reuse in the next patch.
Stable-dep-of: c073d1b070f1 ("ipv6: mcast: use copy-on-write RCU updates in ip6_mc_source()")
--- net/ipv6/ipv6_sockglue.c | 2 -- net/ipv6/mcast.c | 47 +++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 22 deletions(-)
net/ipv6/ipv6_sockglue.c +0 -2
@@ -121,10 +121,8 @@ static bool setsockopt_needs_rtnl(int optname) {switch (optname) {case IPV6_ADDRFORM : -case IPV6_DROP_MEMBERSHIP :case IPV6_JOIN_ANYCAST :case IPV6_LEAVE_ANYCAST : -case MCAST_LEAVE_GROUP :case MCAST_JOIN_SOURCE_GROUP :case MCAST_LEAVE_SOURCE_GROUP :case MCAST_BLOCK_SOURCE :
net/ipv6/mcast.c +27 -20
64 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 576/1102 ipv6: mcast: Dont hold RTNL for MCAST_ socket options.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit e6e14d582dd2cbee362c48a1865f8d03ca0a5611 ]
In ip6_mc_source() and ip6_mc_msfilter(), per-socket mld data is protected by lock_sock() and inet6_dev->mc_lock is also held for some per-interface functions.
ip6_mc_find_dev_rtnl() only depends on RTNL. If we want to remove it, we need to check inet6_dev->dead under mc_lock to close the race with addrconf_ifdown(), as mentioned earlier.
Let's do that and drop RTNL for the rest of MCAST_ socket options.
Note that ip6_mc_msfilter() has unnecessary lock dances and they are integrated into one to avoid the last-minute error and simplify the error handling.
Stable-dep-of: c073d1b070f1 ("ipv6: mcast: use copy-on-write RCU updates in ip6_mc_source()")
--- net/ipv6/ipv6_sockglue.c | 5 --- net/ipv6/mcast.c | 74 ++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 34 deletions(-)
net/ipv6/ipv6_sockglue.c +0 -5
@@ -123,11 +123,6 @@ static bool setsockopt_needs_rtnl(int optname)case IPV6_ADDRFORM :case IPV6_JOIN_ANYCAST :case IPV6_LEAVE_ANYCAST : -case MCAST_JOIN_SOURCE_GROUP : -case MCAST_LEAVE_SOURCE_GROUP : -case MCAST_BLOCK_SOURCE : -case MCAST_UNBLOCK_SOURCE : -case MCAST_MSFILTER :return true; }return false;
net/ipv6/mcast.c +45 -29
169 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 578/1102 ip6_gre: check tunnel info before xmit in ip6gre_tunnel_xmit
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 97cc84dad1d7f68a36b71b69b361d88482707673 ]
Shuangpeng Bai reported a KASAN slab-use-after-free in ip6gre_tunnel_xmit().
The precise KASAN bug was caused by ip6_tnl_xmit() consuming the skb during headroom expansion and returning an error, while ip6gre_tunnel_xmit() still held the stale pointer and called skb_tunnel_info_txcheck(skb) at tx_err. That specific bug was fixed by commit 87f21b59ddc6 ("ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()").
However, calling skb_tunnel_info_txcheck(skb) at the tx_err label after the transmission attempt remains problematic:
Downstream helpers like ip6_tnl_xmit() call skb_scrub_packet(), which drops the skb's metadata_dst before transmission. If an error occurs later during transmit, inspecting skb at tx_err sees a scrubbed dst and misclassifies tx_errors vs tx_dropped.
Commit e5f7e211b6aa ("ip6gre: avoid tx_error when sending MLD/DAD on external tunnels") already handled this correctly in ip6erspan_tunnel_xmit() by checking and caching tun_info before transmit.
Align ip6gre_tunnel_xmit() with ip6erspan_tunnel_xmit() by caching tun_info before xmit and checking it at tx_err.
--- net/ipv6/ip6_gre.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/ipv6/ip6_gre.c +5 -1
@@ -906,6 +906,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)static netdev_tx_t ip6gre_tunnel_xmit (struct sk_buff *skb,struct net_device *dev) { +struct ip_tunnel_info *tun_info =NULL ;struct ip6_tnl *t =netdev_priv (dev);__be16 payload_protocol;int ret;
@@ -916,6 +917,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,if (!ip6_tnl_xmit_ctl (t, &t->parms.laddr, &t->parms.raddr)) goto tx_err; +if (t->parms.collect_md) + tun_info =skb_tunnel_info_txcheck (skb); + payload_protocol =skb_protocol (skb, true);switch (payload_protocol) {case htons (ETH_P_IP ):
@@ -935,7 +939,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,return NETDEV_TX_OK ; tx_err: -if (!t->parms.collect_md || !IS_ERR (skb_tunnel_info_txcheck (skb))) +if (!IS_ERR (tun_info))DEV_STATS_INC (dev, tx_errors);DEV_STATS_INC (dev, tx_dropped);kfree_skb (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 579/1102 tipc: fix NULL deref in tipc_named_node_up() on empty publication list
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tung Nguyen <[email protected]>
[ Upstream commit b3b76e9f4f2476f1135b2ba7743a821db4a0df4b ]
User-space applications can bind a large number of service addresses to one or more sockets. Each binding of a local-scope service address inserts one entry (publication) into the TIPC name table. If the number of these publications exceeds TIPC_MAX_PUBL (65535), protocol service types (such as node state and link state) are no longer inserted into the name table. This causes two issues:
1. User-space applications subscribing to node or link up/down events stop receiving notifications.
2. A NULL pointer dereference can occur:
BUG: kernel NULL pointer dereference, address: 00000000000000d0 ... CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0-rc4-default+ #5 PREEMPT(full) ... RIP: 0010:tipc_named_node_up (./include/linux/skbuff.h:2251 net/tipc/name_distr.c:195 net/tipc/name_distr.c:221) ... Call Trace: <IRQ> tipc_node_write_unlock (net/tipc/node.c:428) tipc_rcv (net/tipc/node.c:934 net/tipc/node.c:2189) tipc_udp_recv (net/tipc/udp_media.c:389)
Thread 1 (tipc_net_finalize) | Thread 2 (named_distribute)
-----------------------------|-----------------------------
| ...
| list_for_each_entry(publ, pls, binding_node) {
| ...
| __skb_queue_tail(list, skb);
| ...
| }
| ...
| hdr = buf_msg(skb_peek_tail(list));
... |
tipc_nametbl_publish(); | If 'tipc_nametbl_publish()' (Thread 1) fails because the number of local publications reaches TIPC_MAX_PUBL, list (Thread 2) will be empty. As a result, NULL is passed to 'buf_msg()', leading to a NULL pointer dereference.
Fix these issues by allowing protocol service types (node state, link state, and topology server) to be inserted into the name table unconditionally. This ensures that users subscribing to these types always receive notifications. In addition, the maximum number of local user publications is reduced to (TIPC_MAX_PUBL - 1). This ensures that the maximum bulk size calculated in tipc_link_set_queue_limits() remains valid.
--- net/tipc/name_table.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-)
net/tipc/name_table.c +26 -4
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk, u32 key) {struct name_table *nt =tipc_name_table (net); +u32 max_user_pub =TIPC_MAX_PUBL -1 ;struct tipc_net *tn =tipc_net (net);struct publication *p =NULL ;struct sk_buff *skb =NULL ; +bool protocol_type = false;u32 rc_dests; +if (ua->sr.type ==TIPC_NODE_STATE || ua->sr.type ==TIPC_LINK_STATE || + ua->sr.type ==TIPC_TOP_SRV ) + protocol_type = true; +spin_lock_bh (&tn->nametbl_lock); +if (protocol_type) + goto insert; -if (nt->local_publ_count >=TIPC_MAX_PUBL ) { -pr_warn ("Bind failed, max limit %u reached\n" ,TIPC_MAX_PUBL ); +/* Reserve one entry for node state service type because it has cluster +* scope and it is distributed in bulk. So, the maximum number of user's +* publications is (TIPC_MAX_PUBL - 1). +*/ +if (nt->local_publ_count >= max_user_pub) { +pr_warn ("Bind failed, max limit %u reached\n" , max_user_pub); goto exit; } +insert: p =tipc_nametbl_insert_publ (net, ua, sk, key);if (p) { - nt->local_publ_count++; +/* Not count node state, link state and topology server types +* so that maximum nt->local_publ_count does not prevent +* protocol service types from being inserted into the name +* table. +*/ +if (!protocol_type) + nt->local_publ_count++; skb =tipc_named_publish (net, p); } rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, p =tipc_nametbl_remove_publ (net, ua, sk, key);if (p) { - nt->local_publ_count--; +if (p->sr.type !=TIPC_NODE_STATE && + p->sr.type !=TIPC_LINK_STATE && + p->sr.type !=TIPC_TOP_SRV ) + nt->local_publ_count--; skb =tipc_named_withdraw (net, p);list_del_init (&p->binding_sock);kfree_rcu (p, rcu);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 580/1102 tipc: Dont send random pad bytes in RESET/ACTIVATE messages
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Laight <[email protected]>
[ Upstream commit 81c600c26302a27852ed8b19c5f2f647ea3555c9 ]
The interface name is passed in a fixed length (TIPC_MAX_IF_NAME) buffer. Replace the strcpy(data, l->if_name) with memcpy() so that the pad bytes are actually written (l->if_name[] is zero padded) rather than sending random bytes from the skb to the remote system.
Replace two other strcpy() with strscpy().
--- net/tipc/link.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/tipc/link.c +3 -3
@@ -506,7 +506,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,snprintf (l->name,sizeof (l->name),"%s:%s-%s:unknown" , self_str, if_name, peer_str); -strcpy (l->if_name, if_name); +strscpy (l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; l->net = net;
@@ -577,7 +577,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,snprintf (l->name,sizeof (l->name),"%s:%s" , tipc_bclink_name, peer_str); }else { -strcpy (l->name, tipc_bclink_name); +strscpy (l->name, tipc_bclink_name); }trace_tipc_link_reset (l,TIPC_DUMP_ALL ,"bclink created!" );tipc_link_reset (l);
@@ -1901,7 +1901,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,msg_set_dest_session (hdr, l->peer_session); }msg_set_max_pkt (hdr, l->advertised_mtu); -strcpy (data, l->if_name); +memcpy (data, l->if_name,TIPC_MAX_IF_NAME );msg_set_size (hdr,INT_H_SIZE +TIPC_MAX_IF_NAME );skb_trim (skb,INT_H_SIZE +TIPC_MAX_IF_NAME ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 581/1102 ipv6: sr: restore network header before routing and forwarding
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 975b5b067f525a1b1338c4a3bee1c46545801518 ]
ipv6_srh_rcv() runs with skb->data at the Segment Routing Header (SRH) while skb_network_header() points at the IPv6 header.
When segments_left > 0, ipv6_srh_rcv() previously restored the skb->data position by pushing sizeof(struct ipv6hdr), assuming the SRH immediately followed the fixed IPv6 header. If another extension header (such as a Hop-by-Hop options header) precedes the SRH, skb_network_offset() remained negative.
This led to two problems:
1. During ip6_route_input(), fib6_rules_early_flow_dissect() invokes __skb_flow_dissect() which passes the negative skb_network_offset() to flow dissection, breaking BPF and C flow dissector logic.
2. If forwarded via ip6_forward() or redirected via act_mirred, downstream handlers (like sch_fragment() or neighbour output) pass the negative offset as an unsigned length, triggering OOB memcpy or buffer overflows.
Fix this by pushing -skb_network_offset(skb) before routing, ensuring skb_network_offset(skb) is 0 for route lookup / flow dissection as well as downstream forwarding. On the loopback path, pull skb_transport_offset(skb) to restore skb->data to the SRH before looping back.
--- net/ipv6/exthdrs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/exthdrs.c +2 -2
@@ -455,7 +455,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb) hdr->segments_left--; addr = hdr->segments + hdr->segments_left; -skb_push (skb,sizeof (struct ipv6hdr )); +skb_push (skb, -skb_network_offset (skb));if (skb->ip_summed ==CHECKSUM_COMPLETE )seg6_update_csum (skb);
@@ -479,7 +479,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb) }ipv6_hdr (skb)->hop_limit--; -skb_pull (skb,sizeof (struct ipv6hdr )); +skb_pull (skb,skb_transport_offset (skb)); goto looped_back; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 582/1102 af_packet: Dont cast tpacket_hdr.tp_len to int in tpacket_parse_header().
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 73e594c19b4f815d8343461cec7074c4713bbde7 ]
syzbot reported BUG() in sock_sendmsg_nosec(). [0]
The problem is that tpacket_parse_header() casts user-provided tpacket_hdr.tp_len, which is u32, to int.
If the length is larger than INT_MAX, the following condition in tpacket_parse_header() passes,
if (unlikely(tp_len > size_max))
and any negative value can be returned to the caller, up to sock_sendmsg_nosec().
The repro set tpacket_hdr.tp_len to 0xfffffdef, which is cast to -EIOCBQUEUED (-529), triggering BUG() in sock_sendmsg_nosec().
*(uint64_t*)0x200000000008 = 0xfffffdef; ... syscall(__NR_write, /*fd=*/r[0], /*buf=*/0x200000000000ul, /*count=*/1ul);
Let's define the local tp_len as u32 in tpacket_parse_header().
[0]:
kernel BUG at net/socket.c:803!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 0 UID: 0 PID: 5628 Comm: syz-executor176 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026 RIP: 0010:sock_sendmsg_nosec+0x145/0x180 net/socket.c:803 Code: 06 67 48 0f b9 3a eb 95 e8 e8 3a 22 f8 48 89 df 4c 89 f6 4c 89 e2 4d 89 fb 2e e8 32 a5 5c 16 e9 51 ff ff ff e8 cc 3a 22 f8 90 <0f> 0b e8 c4 3a 22 f8 48 83 c3 18 48 89 d8 48 c1 e8 03 42 80 3c 28 RSP: 0018:ffffc90003aefb48 EFLAGS: 00010293 RAX: ffffffff89a578d4 RBX: ffff8880764c67c0 RCX: ffff88807fb23e80 RDX: 0000000000000000 RSI: 00000000fffffdef RDI: 00000000fffffdef RBP: 00000000fffffdef R08: ffffc90003aef747 R09: 1ffff9200075dee8 R10: dffffc0000000000 R11: fffff5200075dee9 R12: 0000000000000001 R13: dffffc0000000000 R14: ffffc90003aefbc0 R15: ffffffff8aac4310 FS: 000055559101b400(0000) GS:ffff888124ce0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000200000000210 CR3: 0000000073dca000 CR4: 00000000003526f0 Call Trace: <TASK> __sock_sendmsg net/socket.c:815 [inline] sock_write_iter+0x2de/0x3e0 net/socket.c:1266 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x612/0xba0 fs/read_write.c:687 ksys_write+0x150/0x270 fs/read_write.c:739 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f173130ecb9 Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffd67e44248 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000200000000000 RCX: 00007f173130ecb9 RDX: 0000000000000001 RSI: 0000200000000000 RDI: 0000000000000003 RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd67e44388 R13: 0000000000000002 R14: 00002000000000c0 R15: 0000000000000002 </TASK>
--- net/packet/af_packet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/packet/af_packet.c +3 -2
@@ -2724,7 +2724,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, int size_max, void **data) { union tpacket_uhdr ph; -int tp_len, off; +u32 tp_len; +int off; ph.raw = frame;
@@ -2744,7 +2745,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,break ; }if (unlikely (tp_len > size_max)) { -pr_err ("packet size is too long (%d > %d)\n" , tp_len, size_max); +pr_err ("packet size is too long (%u > %d)\n" , tp_len, size_max);return -EMSGSIZE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 583/1102 staging: fbtft: make dirty_lock IRQ-safe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
[ Upstream commit f576944a59f31bcffff121117ebf452c5dd162b7 ]
fbtft_mkdirty() can be reached from the fbcon rendering path while processing printk() in hardirq context. Meanwhile, dirty_lock is also taken by fbtft_deferred_io() in workqueue context with local interrupts enabled.
Lockdep reports a possible IRQ lock inversion involving dirty_lock and console_owner. A hardirq can interrupt a CPU holding dirty_lock and enter the console rendering path, which can attempt to acquire dirty_lock again.
The following lockdep report was observed on an RK3566 system with CONFIG_PROVE_LOCKING enabled:
WARNING: possible irq lock inversion dependency detected swapper/2/0 just changed the state of lock:
(console_owner){-...}-{0:0}
but this lock took another, HARDIRQ-unsafe lock in the past:
(&par->dirty_lock){+.+.}-{2:2}
CPU0 CPU1
---- ----
lock(&par->dirty_lock);
local_irq_disable();
lock(console_owner);
lock(&par->dirty_lock);
<Interrupt>
lock(console_owner);*** DEADLOCK ***
Use spin_lock_irqsave() for fbtft_mkdirty() and spin_lock_irq() for fbtft_deferred_io(). They only access the dirty line range, so the IRQ-off regions remain short.
--- drivers/staging/fbtft/fbtft-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/staging/fbtft/fbtft-core.c +5 -4
@@ -299,6 +299,7 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) {struct fbtft_par *par = info->par;struct fb_deferred_io *fbdefio = info->fbdefio; +unsigned long flags;/* special case, needed ? */ if (y ==-1 ) {
@@ -307,12 +308,12 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) }/* Mark display lines/area as dirty */ -spin_lock (&par->dirty_lock); +spin_lock_irqsave (&par->dirty_lock, flags);if (y < par->dirty_lines_start) par->dirty_lines_start = y;if (y + height -1 > par->dirty_lines_end) par->dirty_lines_end = y + height -1 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irqrestore (&par->dirty_lock, flags);/* Schedule deferred_io to update display (no-op if already on queue)*/ schedule_delayed_work (&info->deferred_work, fbdefio->delay);
@@ -325,13 +326,13 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflisstruct fb_deferred_io_pageref *pageref;unsigned int y_low =0 , y_high =0 ; -spin_lock (&par->dirty_lock); +spin_lock_irq (&par->dirty_lock); dirty_lines_start = par->dirty_lines_start; dirty_lines_end = par->dirty_lines_end;/* set display line markers as clean */ par->dirty_lines_start = par->info->var.yres -1 ; par->dirty_lines_end =0 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irq (&par->dirty_lock);/* Mark display lines as dirty */ list_for_each_entry (pageref, pagereflist, list) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 584/1102 ALSA: hda/core: Use guard() for mutex locks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 0a930d8732fc076600c4b346a83d9b0a190aeaf0 ]
Replace the manual mutex lock/unlock pairs with guard().
Only code refactoring, and no behavior change.
Stable-dep-of: 32d7226e6105 ("ALSA: hda: restore MFG widget enumeration after core split")
--- sound/hda/ext/hdac_ext_controller.c | 6 ++--- sound/hda/hdac_bus.c | 8 ++----- sound/hda/hdac_component.c | 6 ++--- sound/hda/hdac_device.c | 23 +++++++++---------- sound/hda/hdac_regmap.c | 35 +++++++++-------------------- 5 files changed, 27 insertions(+), 51 deletions(-)
sound/hda/ext/hdac_ext_controller.c +2 -4
@@ -281,7 +281,7 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,unsigned long codec_mask;int ret =0 ; -mutex_lock (&bus->lock); +guard (mutex)(&bus->lock);/* * if we move from 0 to 1, count will be 1 so power up this link
@@ -312,7 +312,6 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, bus->codec_mask = codec_mask; } -mutex_unlock (&bus->lock);return ret; }EXPORT_SYMBOL_GPL (snd_hdac_ext_bus_link_get);
@@ -324,7 +323,7 @@ int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,struct hdac_ext_link *hlink_tmp;bool link_up = false; -mutex_lock (&bus->lock); +guard (mutex)(&bus->lock);/* * if we move from 1 to 0, count will be 0
@@ -350,7 +349,6 @@ int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, } } -mutex_unlock (&bus->lock);return ret; }EXPORT_SYMBOL_GPL (snd_hdac_ext_bus_link_put);
sound/hda/hdac_bus.c +2 -6
@@ -87,12 +87,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);int snd_hdac_bus_exec_verb (struct hdac_bus *bus,unsigned int addr,unsigned int cmd,unsigned int *res) { -int err; - -mutex_lock (&bus->cmd_mutex); - err =snd_hdac_bus_exec_verb_unlocked (bus, addr, cmd, res); -mutex_unlock (&bus->cmd_mutex); -return err; +guard (mutex)(&bus->cmd_mutex); +return snd_hdac_bus_exec_verb_unlocked (bus, addr, cmd, res); }/**
sound/hda/hdac_component.c +2 -4
@@ -70,14 +70,14 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)dev_dbg (bus->dev,"display power %s\n" , enable ?"enable" :"disable" ); -mutex_lock (&bus->lock); +guard (mutex)(&bus->lock);if (enable)set_bit (idx, &bus->display_power_status);else clear_bit (idx, &bus->display_power_status);if (!acomp || !acomp->ops) - goto unlock; +return ;if (bus->display_power_status) {if (!bus->display_power_active) {
@@ -100,8 +100,6 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) bus->display_power_active =0 ; } } - unlock: -mutex_unlock (&bus->lock); }EXPORT_SYMBOL_GPL (snd_hdac_display_power);
sound/hda/hdac_device.c +10 -13
@@ -147,9 +147,9 @@ int snd_hdac_device_register(struct hdac_device *codec) err =device_add (&codec->dev);if (err <0 )return err; -mutex_lock (&codec->widget_lock); - err =hda_widget_sysfs_init (codec); -mutex_unlock (&codec->widget_lock); + scoped_guard(mutex, &codec->widget_lock) { + err =hda_widget_sysfs_init (codec); + }if (err <0 ) {device_del (&codec->dev);return err;
@@ -166,9 +166,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_register);void snd_hdac_device_unregister (struct hdac_device *codec) {if (device_is_registered (&codec->dev)) { -mutex_lock (&codec->widget_lock); -hda_widget_sysfs_exit (codec); -mutex_unlock (&codec->widget_lock); + scoped_guard(mutex, &codec->widget_lock) { +hda_widget_sysfs_exit (codec); + }device_del (&codec->dev);snd_hdac_bus_remove_device (codec->bus, codec); }
@@ -411,25 +411,22 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)* Serialize against multiple threads trying to update the sysfs * widgets array. */ -mutex_lock (&codec->widget_lock); +guard (mutex)(&codec->widget_lock); nums =snd_hdac_get_sub_nodes (codec, codec->afg, &start_nid);if (!start_nid || nums <=0 || nums >=0xff ) {dev_err (&codec->dev,"cannot read sub nodes for FG 0x%02x\n" , codec->afg); - err = -EINVAL ; - goto unlock; +return -EINVAL ; } err =hda_widget_sysfs_reinit (codec, start_nid, nums);if (err <0 ) - goto unlock; +return err; codec->num_nodes = nums; codec->start_nid = start_nid; codec->end_nid = start_nid + nums; -unlock: -mutex_unlock (&codec->widget_lock); -return err; +return 0 ; }EXPORT_SYMBOL_GPL (snd_hdac_refresh_widgets);
sound/hda/hdac_regmap.c +11 -24
@@ -425,15 +425,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);static int reg_raw_write (struct hdac_device *codec,unsigned int reg,unsigned int val) { -int err; - -mutex_lock (&codec->regmap_lock); +guard (mutex)(&codec->regmap_lock);if (!codec->regmap) - err =hda_reg_write (codec, reg, val); +return hda_reg_write (codec, reg, val);else - err =regmap_write (codec->regmap, reg, val); -mutex_unlock (&codec->regmap_lock); -return err; +return regmap_write (codec->regmap, reg, val); }/* a helper macro to call @func_call; retry with power-up if failed */
@@ -466,15 +462,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);static int reg_raw_read (struct hdac_device *codec,unsigned int reg,unsigned int *val,bool uncached) { -int err; - -mutex_lock (&codec->regmap_lock); +guard (mutex)(&codec->regmap_lock);if (uncached || !codec->regmap) - err =hda_reg_read (codec, reg, val); +return hda_reg_read (codec, reg, val);else - err =regmap_read (codec->regmap, reg, val); -mutex_unlock (&codec->regmap_lock); -return err; +return regmap_read (codec->regmap, reg, val); } static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
@@ -515,7 +507,7 @@ static int reg_raw_update(struct hdac_device *codec, unsigned int reg,bool change;int err; -mutex_lock (&codec->regmap_lock); +guard (mutex)(&codec->regmap_lock);if (codec->regmap) { err =regmap_update_bits_check (codec->regmap, reg, mask, val, &change);
@@ -533,7 +525,6 @@ static int reg_raw_update(struct hdac_device *codec, unsigned int reg, } } } -mutex_unlock (&codec->regmap_lock);return err; }
@@ -556,17 +547,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);static int reg_raw_update_once (struct hdac_device *codec,unsigned int reg,unsigned int mask,unsigned int val) { -int err =0 ; -if (!codec->regmap)return reg_raw_update (codec, reg, mask, val); -mutex_lock (&codec->regmap_lock); +guard (mutex)(&codec->regmap_lock);/* Discard any updates to already initialised registers. */ if (!regcache_reg_cached (codec->regmap, reg)) - err =regmap_update_bits (codec->regmap, reg, mask, val); -mutex_unlock (&codec->regmap_lock); -return err; +return regmap_update_bits (codec->regmap, reg, mask, val); +return 0 ; }/**
@@ -593,9 +581,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);*/ void snd_hdac_regmap_sync (struct hdac_device *codec) { -mutex_lock (&codec->regmap_lock); +guard (mutex)(&codec->regmap_lock);if (codec->regmap)regcache_sync (codec->regmap); -mutex_unlock (&codec->regmap_lock); }EXPORT_SYMBOL_GPL (snd_hdac_regmap_sync);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 585/1102 ALSA: hda: restore MFG widget enumeration after core split
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
[ Upstream commit 32d7226e6105c257ef7b3d0ec819f11a81f53b6d ]
Before commit 7639a06c23c7 ("ALSA: hda - Move a part of hda_codec stuff into hdac_device"), widget enumeration selected the function group with
codec->afg ? codec->afg : codec->mfg
and read subordinate nodes from that group.
The core split moved this logic into snd_hdac_refresh_widgets(), but hard-coded codec->afg there. For an MFG-only codec, codec->afg is zero, so the Root Node is queried and codec->start_nid/num_nodes are populated from the function-group range instead of the MFG's subordinate nodes.
Restore the pre-split AFG-or-MFG selection.
--- sound/hda/hdac_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/hda/hdac_device.c +3 -2
@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec)*/ int snd_hdac_refresh_widgets (struct hdac_device *codec) { +hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;hda_nid_t start_nid;int nums, err =0 ;
@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)* widgets array. */ guard (mutex)(&codec->widget_lock); - nums =snd_hdac_get_sub_nodes (codec, codec->afg, &start_nid); + nums =snd_hdac_get_sub_nodes (codec, fg, &start_nid);if (!start_nid || nums <=0 || nums >=0xff ) {dev_err (&codec->dev,"cannot read sub nodes for FG 0x%02x\n" , - codec->afg); + fg);return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 586/1102 s390/boot: Fix physical memory search range
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit a0c798ed4103316c23938bdf625af364fbd38016 ]
search_mem_end() calculates the number of 1MB blocks with a signed int literal. CONFIG_MAX_PHYSMEM_BITS values of 51 and above either overflow the signed int or shift beyond its width. This produces an invalid search range when the binary-search memory detection fallback is used.
Use an unsigned long literal so the full supported physical address range is represented.
--- arch/s390/boot/physmem_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/boot/physmem_info.c +1 -1
@@ -140,7 +140,7 @@ static int tprot(unsigned long addr)static unsigned long search_mem_end (void ) { -unsigned long range =1 << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ +unsigned long range =1UL << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ unsigned long offset =0 ;unsigned long pivot;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 587/1102 s390/boot: Avoid IPL parameter append past command line
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit d76181dfabdaa720703167393704efacba343442 ]
A command line may occupy all but the terminating byte of COMMAND_LINE_SIZE. In that case append_ipl_block_parm() passes a zero size to the IPL parameter conversion helpers and points the destination one byte past early_command_line. The helpers subtract one from the unsigned size and write the converted parameter outside the command line buffer.
Convert the IPL parameter in the command line parsing buffer first. A parameter beginning with '=' can then replace the existing command line regardless of its length, while other parameters are appended only when space remains.
--- arch/s390/boot/ipl_parm.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
arch/s390/boot/ipl_parm.c +12 -14
@@ -21,6 +21,7 @@ struct parmarea parmarea __section(".parmarea") = { };char __bootdata (early_command_line )[COMMAND_LINE_SIZE ]; +static char command_line_buf[COMMAND_LINE_SIZE ];unsigned int __bootdata_preserved (zlib_dfltcc_support ) =ZLIB_DFLTCC_FULL ;struct ipl_parameter_block __bootdata_preserved (ipl_block );
@@ -148,31 +149,29 @@ static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,static void append_ipl_block_parm (void ) { -char *parm, *delim; -size_t len, rc =0 ; +size_t len, extra =0 ; +char *delim; len =strlen (early_command_line); - - delim = early_command_line + len;/* '\0' character position */ - parm = early_command_line + len +1 ;/* append right after '\0' */ + delim = early_command_line + len;/* '\0' character position */ switch (ipl_block.pb0_hdr.pbt) {case IPL_PBT_CCW : - rc =ipl_block_get_ascii_vmparm ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_vmparm (command_line_buf,sizeof (command_line_buf), &ipl_block);break ;case IPL_PBT_FCP :case IPL_PBT_NVME :case IPL_PBT_ECKD : - rc =ipl_block_get_ascii_scpdata ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_scpdata (command_line_buf,sizeof (command_line_buf), &ipl_block);break ; } -if (rc) { -if (*parm =='=' ) -memmove (early_command_line, parm +1 , rc); -else +if (extra) { +if (command_line_buf[0 ] =='=' ) { +memmove (early_command_line, command_line_buf +1 , extra); + }else if (len <COMMAND_LINE_SIZE -2 ) { *delim =' ' ;/* replace '\0' with space */ +sized_strscpy (delim +1 , command_line_buf,COMMAND_LINE_SIZE - len -1 ); + } } }
@@ -258,7 +257,6 @@ static void modify_fac_list(char *str)check_cleared_facilities (); } -static char command_line_buf[COMMAND_LINE_SIZE ];void parse_boot_command_line (void ) {char *param, *val;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 588/1102 ASoC: amd: renoir: fix disable_pdm_interrupts() to clear mask bits
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 0c06c4ce0206290c9a934a1e7196aaa86adfe018 ]
disable_pdm_interrupts() uses |= ~PDM_DMA_INTR_MASK which sets all bits except the PDM DMA interrupt bit instead of clearing only the PDM DMA interrupt bit. Use &= ~PDM_DMA_INTR_MASK to clear only the target bit.
--- sound/soc/amd/renoir/acp3x-pdm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/amd/renoir/acp3x-pdm-dma.c +1 -1
@@ -104,7 +104,7 @@ static void disable_pdm_interrupts(void __iomem *acp_base)u32 ext_int_ctrl; ext_int_ctrl =rn_readl (acp_base +ACP_EXTERNAL_INTR_CNTL ); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK ; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK ;rn_writel (ext_int_ctrl, acp_base +ACP_EXTERNAL_INTR_CNTL ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 589/1102 ASoC: amd: yc: fix memory leak in acp6x_pdm_dma_close()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 1b67e0d3b9691d7b6b74e18960ddd2be24f9dc9d ]
acp6x_pdm_dma_close() does not free the runtime->private_data buffer allocated in acp6x_pdm_dma_open(). Add the missing kfree.
--- sound/soc/amd/yc/acp6x-pdm-dma.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/amd/yc/acp6x-pdm-dma.c +2 -0
@@ -275,9 +275,11 @@ static int acp6x_pdm_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) {struct pdm_dev_data *adata =dev_get_drvdata (component->dev); +struct snd_pcm_runtime *runtime = substream->runtime;acp6x_disable_pdm_interrupts (adata->acp6x_base); adata->capture_stream =NULL ; +kfree (runtime->private_data);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 590/1102 drm/xe/oa: Remove sysfs entry on idr_alloc failure in xe_oa_add_config_ioctl()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Yao <[email protected]>
[ Upstream commit 3663c8d1f31e65771bd73ee3259f35fd397f9933 ]
If idr_alloc() fails after create_dynamic_oa_sysfs_entry() has succeeded, the error path frees the OA config without removing the metrics sysfs group.
Remove the sysfs group before releasing the config, and fix up the misleading error message copied from the sysfs creation failure path.
--- drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_oa.c +4 -2
@@ -2389,9 +2389,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi oa_config->id =idr_alloc (&oa->metrics_idr, oa_config,1 ,0 ,GFP_KERNEL );if (oa_config->id <0 ) { -drm_dbg (&oa->xe->drm,"Failed to create sysfs entry for OA config\n" ); +drm_dbg (&oa->xe->drm,"Failed to allocate id for OA config\n" ); err = oa_config->id; - goto sysfs_err; + goto id_alloc_err; } id = oa_config->id;
@@ -2402,6 +2402,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fireturn id; +id_alloc_err: +sysfs_remove_group (oa->metrics_kobj, &oa_config->sysfs_metric); sysfs_err:mutex_unlock (&oa->metrics_lock); reg_err:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 591/1102 btrfs: detach failed sprout device from transaction update list
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit c93b3c43df561cd9f592cee20ae058b563f9e5b6 ]
When creating the first metadata chunk for a sprout filesystem, create_chunk() adds the new device to the transaction dev_update_list through device->post_commit_list.
If the subsequent system chunk creation fails, btrfs_init_new_device() aborts the transaction and releases the device while post_commit_list is still linked. This triggers a warning in btrfs_free_device() and leaves the transaction list referencing freed memory.
Detach the device while holding chunk_mutex before releasing it.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -2889,6 +2889,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_pathbtrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex);mutex_lock (&fs_info->chunk_mutex); +if (!list_empty (&device->post_commit_list)) +list_del_init (&device->post_commit_list);list_del_rcu (&device->dev_list);list_del (&device->dev_alloc_list); fs_info->fs_devices->num_devices--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 592/1102 btrfs: restore active device pointers after failed sprout
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit e0b54613aabeb8e9da597f23b90c6a03d0981986 ]
btrfs_init_new_device() switches latest_dev and possibly s_bdev from the seed device to the new sprout device before creating the first writable chunks.
If chunk creation or the subsequent sprout setup fails, the error path releases the new device without switching those pointers back. btrfs_show_devname() can then dereference the freed latest_dev and crash.
Restore the active device pointers to the latest seed device before removing and releasing the failed sprout device.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -2888,6 +2888,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path error_sysfs:btrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex); +if (seeding_dev) +btrfs_assign_next_active_device (device, seed_devices->latest_dev);mutex_lock (&fs_info->chunk_mutex);if (!list_empty (&device->post_commit_list))list_del_init (&device->post_commit_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 593/1102 bonding: alb: fix uninitialized transport header access in alb_determine_nd()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 70f3995830d3f1e79faa14eb0605914f778feca9 ]
alb_determine_nd() uses icmp6_hdr(skb) to inspect ICMPv6 headers. However, in xmit paths (e.g. packets sent via AF_PACKET / raw sockets or forwarded packets), skb->transport_header is not guaranteed to be initialized. While pskb_network_may_pull() ensures the packet data is linear starting from the network header, it does not set or adjust the transport header offset.
Dereferencing icmp6_hdr(skb) can therefore access out-of-bounds memory.
Fetch the icmp6hdr directly after ipv6hdr following pskb_network_may_pull(), and reload ipv6hdr in case pskb_may_pull() reallocated skb->head. Also remove the unused bond argument from alb_determine_nd().
--- drivers/net/bonding/bond_alb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/net/bonding/bond_alb.c +7 -6
@@ -1282,10 +1282,10 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) }/* determine if the packet is NA or NS */ -static bool alb_determine_nd (struct sk_buff *skb,struct bonding *bond) +static bool alb_determine_nd (struct sk_buff *skb) { -struct ipv6hdr *ip6hdr; -struct icmp6hdr *hdr; +const struct ipv6hdr *ip6hdr; +const struct icmp6hdr *hdr;if (!pskb_network_may_pull (skb,sizeof (*ip6hdr)))return true;
@@ -1297,7 +1297,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)if (!pskb_network_may_pull (skb,sizeof (*ip6hdr) +sizeof (*hdr)))return true; - hdr =icmp6_hdr (skb); + ip6hdr =ipv6_hdr (skb); + hdr = (const struct icmp6hdr *)(ip6hdr +1 );return hdr->icmp6_type ==NDISC_NEIGHBOUR_ADVERTISEMENT || hdr->icmp6_type ==NDISC_NEIGHBOUR_SOLICITATION ; }
@@ -1382,7 +1383,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,if (!is_multicast_ether_addr (eth_data->h_dest)) {switch (skb->protocol) {case htons (ETH_P_IPV6 ): -if (alb_determine_nd (skb, bond)) +if (alb_determine_nd (skb))break ; fallthrough;case htons (ETH_P_IP ):
@@ -1468,7 +1469,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,break ; } -if (alb_determine_nd (skb, bond)) { +if (alb_determine_nd (skb)) { do_tx_balance = false;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 594/1102 perf: RISC-V: check cpu_hw_evt before dereference in overflow IRQ
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit f643f520c4c6998fa27dce90dd3b3ff6414e0bae ]
The overflow IRQ handler dereferences cpu_hw_evt before the null check. Move the check first. Defensive only; the cookie is valid on the normal path today.
--- drivers/perf/riscv_pmu_sbi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/perf/riscv_pmu_sbi.c +3 -1
@@ -958,11 +958,13 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)u64 overflowed_ctrs =0 ;struct cpu_hw_events *cpu_hw_evt = dev;u64 start_clock =sched_clock (); -struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; +struct riscv_pmu_snapshot_data *sdata;if (WARN_ON_ONCE (!cpu_hw_evt))return IRQ_NONE ; + sdata = cpu_hw_evt->snapshot_addr; +/* Firmware counter don't support overflow yet */ fidx =find_first_bit (cpu_hw_evt->used_hw_ctrs,RISCV_MAX_COUNTERS );if (fidx ==RISCV_MAX_COUNTERS ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 595/1102 scsi: mpt3sas: Avoid out-of-bounds cpumask_of_node() call in _base_assign_reply_queues()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivy Lopez <[email protected]>
[ Upstream commit e0d26fe176a8db6ccad4ab38c5bab29391c1946b ]
dev_to_node() can return NUMA_NO_NODE (-1) on systems without NUMA topology information for the PCI device, such as single-socket boards that don't expose device-to-node affinity. Passing -1 directly into cpumask_of_node() indexes node_to_cpumask_map[-1], an out-of-bounds array read caught by UBSAN:
UBSAN: array-index-out-of-bounds in arch/x86/include/asm/topology.h:72:28 index -1 is out of range for type 'cpumask *[1024]'
Fall back to cpu_online_mask when no NUMA node is available, rather than assuming dev_to_node() always returns a valid node index.
--- drivers/scsi/mpt3sas/mpt3sas_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/scsi/mpt3sas/mpt3sas_base.c +4 -1
@@ -3227,7 +3227,10 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)* corresponding to high iops queues. */ if (ioc->high_iops_queues) { - mask =cpumask_of_node (dev_to_node (&ioc->pdev->dev)); +int node =dev_to_node (&ioc->pdev->dev); + + mask = (node ==NUMA_NO_NODE ) ? + cpu_online_mask :cpumask_of_node (node);for (index =0 ; index < ioc->high_iops_queues; index++) { irq =pci_irq_vector (ioc->pdev, index);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 596/1102 perf/core: Skip empty AUX records with only format flags
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Yan <[email protected]>
[ Upstream commit 8a7f5b5e860b5c113ca99acd5b1e9074f5c5af3c ]
perf_aux_output_end() emits a PERF_RECORD_AUX when the recorded size is nonzero or when any flag other than PERF_AUX_FLAG_OVERWRITE is set.
PMU format flags describe how an AUX payload is encoded. TRBE driver sets PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW for raw trace buffers, causing an AUX record to be emitted even when no trace data.
This is noticeable when tracing a task with strace. Ptrace stops repeatedly end empty AUX transactions, producing many zero-sized PERF_RECORD_AUX records. For example:
perf record -e cs_etm//u -m,128M -- strace ls
perf script -D 2>&1 |
awk '/PERF_RECORD_AUX offset/ {
for (i = 1; i <= NF; i++)
if ($i == "size:" && $(i + 1) == "0")
count++
}
END { print count }'
165This recording contains 165 zero-sized AUX records which provide no useful information to userspace.
Ignore PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK, together with PERF_AUX_FLAG_OVERWRITE, when deciding whether an empty AUX record is useful. Zero-sized records carrying TRUNCATED, PARTIAL or COLLISION are still emitted.
--- kernel/events/ring_buffer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/events/ring_buffer.c +7 -2
@@ -507,7 +507,10 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)/* * Only send RECORD_AUX if we have something useful to communicate * -* Note: the OVERWRITE records by themselves are not considered +* PMU_FORMAT bits identify the PMU type rather than an AUX event +* has occurred, so ignore them for zero-sized records. +* +* The OVERWRITE records by themselves are not considered * useful, as they don't communicate any *new* information, * aside from the short-lived offset, that becomes history at * the next event sched-in and therefore isn't useful.
@@ -516,7 +519,9 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)* offset. So, from now on we don't output AUX records that * have *only* OVERWRITE flag set. */ -if (size || (handle->aux_flags & ~(u64 )PERF_AUX_FLAG_OVERWRITE )) +if (size || + (handle->aux_flags & ~(u64)(PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK | +PERF_AUX_FLAG_OVERWRITE )))perf_event_aux_event (handle->event, aux_head, size, handle->aux_flags);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 597/1102 locking/lockdep: Invalidate stale class_cache entries for zapped classes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 02c6be7d675b21d81f0ba3a524346850a8c0e3bf ]
syzbot reported a lockdep splat hitting DEBUG_LOCKS_WARN_ON(1) in hlock_class() due to an invalid class_idx:
WARNING: kernel/locking/lockdep.c:238 at __lock_acquire+0x382/0x2cf0 kernel/locking/lockdep.c:5203 Workqueue: wg-crypt-wg0 wg_packet_tx_worker RIP: 0010:hlock_class kernel/locking/lockdep.c:238 [inline] RIP: 0010:check_wait_context kernel/locking/lockdep.c:4870 [inline] RIP: 0010:__lock_acquire+0x389/0x2cf0 kernel/locking/lockdep.c:5203 Call Trace: <IRQ> lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5886 _raw_spin_lock+0x2e/0x40 kernel/locking/spinlock.c:173 tcp_tsq_handler+0x29/0x200 net/ipv4/tcp_output.c:1291 tcp_tsq_workfn+0x384/0x410 net/ipv4/tcp_output.c:1325 ...
When a lock class is zapped (e.g. during module unload or key unregistration), zap_class() clears the class's bit in lock_classes_in_use and removes it from the class hash table. However, existing lockdep_map instances embedded in data structures may still retain a pointer to the zapped class in their class_cache[] array.
When __lock_acquire() subsequently runs on such a lock, it finds lock->class_cache[subclass] != NULL, skipping register_lock_class() and assigning hlock->class_idx to the index of the zapped class. When check_wait_context() or hlock_class() inspects the held_lock, it finds !test_bit(class_idx, lock_classes_in_use) and warns. Furthermore, if the zapped slot is subsequently re-allocated to an unrelated lock key, the stale class_cache entry would erroneously match the unrelated class (ABA issue).
Add lock_class_cache_is_valid() to validate that the cached class is within lock_classes bounds, still allocated in lock_classes_in_use (using uninstrumented arch_test_bit() in __always_inline context so it is safe in noinstr contexts like match_held_lock()), and that class->key matches the expected subkey (taking lockdep_set_subclass() overrides into account). Also use READ_ONCE()/WRITE_ONCE() when accessing class_cache[]. If the entry is invalid or stale, fall back to register_lock_class() / look_up_lock_class().
--- kernel/locking/lockdep.c | 50 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/locking/lockdep.c +42 -8
@@ -944,6 +944,34 @@ look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass)return NULL ; } +static __always_inline bool lock_class_cache_is_valid(const struct lockdep_map *lock, +const struct lock_class *class, +unsigned int subclass) +{ +unsigned int class_subclass; + +if (!class) +return false; + +if (unlikely (class < lock_classes || class >= lock_classes +MAX_LOCKDEP_KEYS )) +return false; + +if (unlikely (!arch_test_bit (class - lock_classes, lock_classes_in_use))) +return false; + +if (unlikely (!lock->key)) +return false; + + class_subclass = subclass ? subclass : class->subclass; +if (unlikely (class_subclass >=MAX_LOCKDEP_SUBCLASSES )) +return false; + +if (unlikely (READ_ONCE (class->key) != lock->key->subkeys + class_subclass)) +return false; + +return true; +} +/* * Static locks do not have their class-keys yet - for them the key is * the lock object itself. If the lock is in the per cpu area, the
@@ -1375,9 +1403,9 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) out_set_class_cache:if (!subclass || force) - lock->class_cache[0 ] = class; +WRITE_ONCE (lock->class_cache[0 ], class);else if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - lock->class_cache[subclass] = class; +WRITE_ONCE (lock->class_cache[subclass], class);/* * Hash collision, did we smoke some? We found a class with a matching
@@ -4913,7 +4941,7 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,int i;for (i =0 ; i <NR_LOCKDEP_CACHING_CLASSES ; i++) - lock->class_cache[i] =NULL ; +WRITE_ONCE (lock->class_cache[i],NULL );#ifdef CONFIG_LOCK_STAT lock->cpu =raw_smp_processor_id ();
@@ -4978,12 +5006,15 @@ EXPORT_SYMBOL_GPL(__lockdep_no_track__);void lockdep_set_lock_cmp_fn (struct lockdep_map *lock,lock_cmp_fn cmp_fn,lock_print_fn print_fn) { -struct lock_class *class = lock->class_cache[0 ]; +struct lock_class *class =READ_ONCE (lock->class_cache[0 ]);unsigned long flags;raw_local_irq_save (flags);lockdep_recursion_inc (); +if (!lock_class_cache_is_valid (lock, class,0 )) + class =NULL ; +if (!class) class =register_lock_class (lock,0 ,0 );
@@ -5068,8 +5099,11 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (!prove_locking || lock->key == &__lockdep_no_validate__) check =0 ; -if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - class = lock->class_cache[subclass]; +if (subclass <NR_LOCKDEP_CACHING_CLASSES ) { + class =READ_ONCE (lock->class_cache[subclass]); +if (!lock_class_cache_is_valid (lock, class, subclass)) + class =NULL ; + }/* * Not cached? */
@@ -5272,9 +5306,9 @@ static noinstr int match_held_lock(const struct held_lock *hlock,return 1 ;if (hlock->references) { -const struct lock_class *class = lock->class_cache[0 ]; +const struct lock_class *class =READ_ONCE (lock->class_cache[0 ]); -if (!class) +if (!lock_class_cache_is_valid (lock, class,0 )) class =look_up_lock_class (lock,0 );/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 598/1102 octeontx2-af: Fix limiting SRIOV VF count logic
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sunil Goutham <[email protected]>
[ Upstream commit f695390ea63941a9e412bf1f3afe65ab245fc681 ]
When RVU PF0/AF's VFs are SDP instead of LBK, limiting the VF count based on the LBK channel count is incorrect.
Apply LBK channel-based VF limits only when the VF device ID matches the LBK RVU AFVF device.
--- .../net/ethernet/marvell/octeontx2/af/rvu.c | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +19 -14
@@ -3280,6 +3280,8 @@ int rvu_get_num_lbk_chans(void)return ret; } +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 +static int rvu_enable_sriov (struct rvu *rvu) {struct pci_dev *pdev = rvu->pdev;
@@ -3298,24 +3300,27 @@ static int rvu_enable_sriov(struct rvu *rvu)return 0 ;pci_read_config_word (pdev, pos +PCI_SRIOV_VF_DID , &rvu->vf_devid); - chans =rvu_get_num_lbk_chans (); -if (chans <0 ) -return chans; - vfs =pci_sriov_get_totalvfs (pdev); - -/* Limit VFs in case we have more VFs than LBK channels available. */ -if (vfs > chans) - vfs = chans; -if (!vfs)return 0 ; -/* LBK channel number 63 is used for switching packets between -* CGX mapped VFs. Hence limit LBK pairs till 62 only. -*/ -if (vfs >62 ) - vfs =62 ; +if (rvu->vf_devid ==PCI_DEVID_OCTEONTX2_RVU_AFVF ) { + chans =rvu_get_num_lbk_chans (); +if (chans <0 ) +return chans; + +/* The last LBK channel is reserved for switching packets between +* CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF +* count to available LBK channels minus 2. +*/ + vfs =min (vfs, chans -2 ); + +if (vfs <=0 ) { +dev_warn (&pdev->dev, +"Skipping SRIOV enablement, not enough LBK channels available\n" ); +return 0 ; + } + }/* Save VFs number for reference in VF interrupts handlers. * Since interrupts might start arriving during SRIOV enablement
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 599/1102 ALSA: rawmidi: Expose the tied device number in info ioctl
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit bdf46443f350dd5d226fd528a5a5954ff762f591 ]
The UMP legacy rawmidi is derived from the UMP rawmidi, but currently there is no way to know which device is involved in other side.
This patch extends the rawmidi info ioctl to show the tied device number. As default it stores -1, indicating that no tied device.
Stable-dep-of: adeee7187694 ("ALSA: ump: do not touch legacy_rmidi before it exists")
--- Documentation/sound/designs/midi-2.0.rst | 5 +++++ include/sound/rawmidi.h | 1 + include/uapi/sound/asound.h | 5 ++++- sound/core/rawmidi.c | 2 ++ sound/core/ump.c | 3 +++ 5 files changed, 15 insertions(+), 1 deletion(-)
Documentation/sound/designs/midi-2.0.rst +5 -0
@@ -293,6 +293,11 @@ Rawmidi API Extensions status 0x05). When UMP core receives such a message, it updates the UMP EP info and the corresponding sequencer clients as well. +* The legacy rawmidi device number is found in the new `tied_device` + field of the rawmidi info. + On the other hand, the UMP rawmidi device number is found in + `tied_device` field of the legacy rawmidi info, too. + Control API Extensions ======================
include/sound/rawmidi.h +1 -0
@@ -118,6 +118,7 @@ struct snd_rawmidi {struct list_head list;unsigned int device;/* device number */ unsigned int info_flags;/* SNDRV_RAWMIDI_INFO_XXXX */ +unsigned int tied_device;char id[64 ];char name[80 ];
include/uapi/sound/asound.h +4 -1
@@ -729,6 +729,8 @@ enum {#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004#define SNDRV_RAWMIDI_INFO_UMP 0x00000008 +#define SNDRV_RAWMIDI_DEVICE_UNKNOWN -1 +struct snd_rawmidi_info {unsigned int device;/* RO/WR (control): device number */ unsigned int subdevice;/* RO/WR (control): subdevice number */
@@ -740,7 +742,8 @@ struct snd_rawmidi_info {unsigned char subname[32 ];/* name of active or selected subdevice */ unsigned int subdevices_count;unsigned int subdevices_avail; -unsigned char reserved[64 ];/* reserved for future use */ +int tied_device;/* R: tied rawmidi device (UMP/legacy) */ +unsigned char reserved[60 ];/* reserved for future use */ };#define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0)
sound/core/rawmidi.c +2 -0
@@ -635,6 +635,7 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->subdevices_count = substream->pstr->substream_count; info->subdevices_avail = (substream->pstr->substream_count - substream->pstr->substream_opened); + info->tied_device = rmidi->tied_device;return 0 ; }
@@ -1834,6 +1835,7 @@ int snd_rawmidi_init(struct snd_rawmidi *rmidi,INIT_LIST_HEAD (&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT ].substreams);INIT_LIST_HEAD (&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT ].substreams); rmidi->info_flags = info_flags; + rmidi->tied_device =SNDRV_RAWMIDI_DEVICE_UNKNOWN ;if (id !=NULL )strscpy (rmidi->id, id,sizeof (rmidi->id));
sound/core/ump.c +3 -0
@@ -1315,6 +1315,9 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, ump->legacy_rmidi = rmidi;update_legacy_names (ump); + rmidi->tied_device = ump->core.device; + ump->core.tied_device = rmidi->device; +ump_dbg (ump,"Created a legacy rawmidi #%d (%s)\n" , device, id);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 600/1102 ALSA: rawmidi: Show substream activity in info ioctl
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit b8fefed73a952a33521ad416fb39683abd87454b ]
The UMP legacy rawmidi may turn on/off the substream dynamically depending on the UMP Function Block information. So far, there was no direct way to know whether the substream is disabled (inactive) or not; at most one can take a look at the substream name string or try to open and get -ENODEV.
This patch extends the rawmidi info ioctl to show the current inactive state of the given substream. When the selected substream is inactive, info flags field contains the new bit flag SNDRV_RAWMIDI_INFO_STREAM_INACTIVE.
Stable-dep-of: adeee7187694 ("ALSA: ump: do not touch legacy_rmidi before it exists")
--- Documentation/sound/designs/midi-2.0.rst | 6 ++++++ include/sound/rawmidi.h | 1 + include/uapi/sound/asound.h | 1 + sound/core/rawmidi.c | 2 ++ sound/core/ump.c | 9 +++++---- 5 files changed, 15 insertions(+), 4 deletions(-)
Documentation/sound/designs/midi-2.0.rst +6 -0
@@ -298,6 +298,12 @@ Rawmidi API Extensions On the other hand, the UMP rawmidi device number is found in `tied_device` field of the legacy rawmidi info, too. +* Each substream of the legacy rawmidi may be enabled / disabled + dynamically depending on the UMP FB state. + When the selected substream is inactive, it's indicated by the bit + 0x10 (`SNDRV_RAWMIDI_INFO_STREAM_INACTIVE`) in the `flags` field of + the legacy rawmidi info. + Control API Extensions ======================
include/sound/rawmidi.h +1 -0
@@ -89,6 +89,7 @@ struct snd_rawmidi_substream {unsigned int framing;/* whether to frame input data */ unsigned int clock_type;/* clock source to use for input framing */ int use_count;/* use counter (for output) */ +bool inactive;/* inactive substream (for UMP legacy) */ size_t bytes;spinlock_t lock;struct snd_rawmidi *rmidi;
include/uapi/sound/asound.h +1 -0
@@ -728,6 +728,7 @@ enum {#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004#define SNDRV_RAWMIDI_INFO_UMP 0x00000008 +#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010#define SNDRV_RAWMIDI_DEVICE_UNKNOWN -1
sound/core/rawmidi.c +2 -0
@@ -629,6 +629,8 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->subdevice = substream->number; info->stream = substream->stream; info->flags = rmidi->info_flags; +if (substream->inactive) + info->flags |=SNDRV_RAWMIDI_INFO_STREAM_INACTIVE ;strcpy (info->id, rmidi->id);strcpy (info->name, rmidi->name);strcpy (info->subname, substream->name);
sound/core/ump.c +5 -4
@@ -1250,8 +1250,8 @@ static int fill_legacy_mapping(struct snd_ump_endpoint *ump)return num; } -static void fill_substream_names (struct snd_ump_endpoint *ump, -struct snd_rawmidi *rmidi,int dir) +static void update_legacy_substreams (struct snd_ump_endpoint *ump, +struct snd_rawmidi *rmidi,int dir) {struct snd_rawmidi_substream *s;const char *name;
@@ -1265,6 +1265,7 @@ static void fill_substream_names(struct snd_ump_endpoint *ump,scnprintf (s->name,sizeof (s->name),"Group %d (%.16s)%s" , idx +1 , name, ump->groups[idx].active ?"" :" [Inactive]" ); + s->inactive = !ump->groups[idx].active; } }
@@ -1272,8 +1273,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; -fill_substream_names (ump, rmidi,SNDRV_RAWMIDI_STREAM_INPUT ); -fill_substream_names (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); +update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_INPUT ); +update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); } int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 601/1102 ALSA: ump: Copy FB name string more safely
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit e3f035edadcc1c5901311c03d098bc3dedc5c525 ]
The UMP group names are referred as the corresponding sequencer port names, hence they should be proper ASCII strings. OTOH, the UMP group names are composed from the UMP FB strings that are received from the device; i.e. a device may give some bogus letters and we can't trust them fully.
To assure that the group names consist of the proper ASCII strings, replace the normal string copy and append operations with special ones that strip the non-printable letters.
Stable-dep-of: adeee7187694 ("ALSA: ump: do not touch legacy_rmidi before it exists")
--- sound/core/ump.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-)
sound/core/ump.c +31 -9
@@ -53,6 +53,34 @@ static inline void update_legacy_names(struct snd_ump_endpoint *ump) }#endif +/* copy a string safely with stripping non-printable letters */ +static void safe_copy_string (void *dst,size_t max_dst_size, +const void *src,size_t max_src_size) +{ +const unsigned char *s = src; +unsigned char *d = dst; + +if (!max_dst_size--) +return ; +for (s = src; max_dst_size && *s && max_src_size--; s++) { +if (!isascii (*s) || !isprint (*s)) +continue ; + *d++ = *s; + max_dst_size--; + } + *d =0 ; +} + +/* append a string safely with stripping non-printable letters */ +static void safe_append_string (void *dst,size_t max_dst_size, +const void *src,size_t max_src_size) +{ +unsigned char *d = dst; +size_t len =strlen (d); + +safe_copy_string (d + len, max_dst_size - len, src, max_src_size); +} +static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = { .dev_register = snd_ump_dev_register, .dev_unregister = snd_ump_dev_unregister,
@@ -565,16 +593,10 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) }if (!*fb->info.name)continue ; -if (!*group->name) { -/* store the first matching name */ -strscpy (group->name, fb->info.name, -sizeof (group->name)); - }else { -/* when overlapping, concat names */ +if (*group->name)strlcat (group->name,", " ,sizeof (group->name)); -strlcat (group->name, fb->info.name, -sizeof (group->name)); - } +safe_append_string (group->name,sizeof (group->name), + fb->info.name,sizeof (fb->info.name)); } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 602/1102 ALSA: ump: Copy safe string name to rawmidi
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 85e8d66f30f967cda91b3ee6dfcb0a0c2391e612 ]
The UMP helper didn't set up the rawmidi name string by itself but left it to the driver. But since the only user (USB MIDI2 driver) picks up the UMP info name string to the rawmidi name as default, it's better to set up in the UMP core side.
Meanwhile, UMP receives the EP name string from the device, and it might contain garbage letters. We should purify the string to be usable for the kernel as done previously for UMP Group names.
This implements the copy of the UMP info name string into the rawmidi name at the creation of UMP EP object in a safe way to strip the non-ASCII or non-printable characters. Also, change the reference from the legacy rawmidi and other places to rawmidi name field instead of ump info; this assures the sane strings.
Stable-dep-of: adeee7187694 ("ALSA: ump: do not touch legacy_rmidi before it exists")
--- sound/core/ump.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
sound/core/ump.c +11 -2
@@ -740,6 +740,13 @@ static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,return 1 ;/* finished */ } +/* set up the core rawmidi name from UMP EP name string */ +static void ump_set_rawmidi_name (struct snd_ump_endpoint *ump) +{ +safe_copy_string (ump->core.name,sizeof (ump->core.name), + ump->info.name,sizeof (ump->info.name)); +} +/* handle EP name stream message; update the UMP name string */ static int ump_handle_ep_name_msg (struct snd_ump_endpoint *ump,const union snd_ump_stream_msg *buf)
@@ -1067,6 +1074,8 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)if (err <0 )ump_dbg (ump,"Unable to get UMP EP name string\n" ); +ump_set_rawmidi_name (ump); +/* Request Endpoint Product ID */ err =ump_req_msg (ump, msg,UMP_STREAM_MSG_REQUEST_PRODUCT_ID ,UMP_STREAM_MSG_STATUS_PRODUCT_ID );
@@ -1283,7 +1292,7 @@ static void update_legacy_substreams(struct snd_ump_endpoint *ump, idx = ump->legacy_mapping[s->number]; name = ump->groups[idx].name;if (!*name) - name = ump->info.name; + name = ump->core.name;scnprintf (s->name,sizeof (s->name),"Group %d (%.16s)%s" , idx +1 , name, ump->groups[idx].active ?"" :" [Inactive]" );
@@ -1331,7 +1340,7 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,snd_rawmidi_set_ops (rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT , &snd_ump_legacy_output_ops);snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , - ump->info.name); + ump->core.name); rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP ; rmidi->ops = &snd_ump_legacy_ops; rmidi->private_data = ump;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 603/1102 ALSA: ump: Update rawmidi name per EP name update
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit aca565359596c5998cab1cc72f61aa83b3ccd152 ]
The rawmidi name string should be updated dynamically when the device receives the UMP EP name update, too. Both the core and legacy rawmidi names are updated.
Stable-dep-of: adeee7187694 ("ALSA: ump: do not touch legacy_rmidi before it exists")
--- sound/core/ump.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-)
sound/core/ump.c +23 -4
@@ -37,6 +37,7 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, u32 *buffer, int count);static void process_legacy_input (struct snd_ump_endpoint *ump,const u32 *src,int words); +static void ump_legacy_set_rawmidi_name (struct snd_ump_endpoint *ump);static void update_legacy_names (struct snd_ump_endpoint *ump);#else static inline int process_legacy_output(struct snd_ump_endpoint *ump,
@@ -48,6 +49,9 @@ static inline void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words) { } +static inline void ump_legacy_set_rawmidi_name (struct snd_ump_endpoint *ump) +{ +}static inline void update_legacy_names (struct snd_ump_endpoint *ump) { }
@@ -751,8 +755,16 @@ static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump)static int ump_handle_ep_name_msg (struct snd_ump_endpoint *ump,const union snd_ump_stream_msg *buf) { -return ump_append_string (ump, ump->info.name,sizeof (ump->info.name), - buf->raw,2 ); +int ret; + + ret =ump_append_string (ump, ump->info.name,sizeof (ump->info.name), + buf->raw,2 ); +if (ret && ump->parsed) { +ump_set_rawmidi_name (ump); +ump_legacy_set_rawmidi_name (ump); + } + +return ret; }/* handle EP product id stream message; update the UMP product_id string */
@@ -1308,6 +1320,14 @@ static void update_legacy_names(struct snd_ump_endpoint *ump)update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); } +static void ump_legacy_set_rawmidi_name (struct snd_ump_endpoint *ump) +{ +struct snd_rawmidi *rmidi = ump->legacy_rmidi; + +snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , + ump->core.name); +} +int snd_ump_attach_legacy_rawmidi (struct snd_ump_endpoint *ump,char *id,int device) {
@@ -1339,12 +1359,11 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,if (output)snd_rawmidi_set_ops (rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT , &snd_ump_legacy_output_ops); -snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , - ump->core.name); rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP ; rmidi->ops = &snd_ump_legacy_ops; rmidi->private_data = ump; ump->legacy_rmidi = rmidi; +ump_legacy_set_rawmidi_name (ump);update_legacy_names (ump); rmidi->tied_device = ump->core.device;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 604/1102 ALSA: ump: do not touch legacy_rmidi before it exists
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingyu Zhang <[email protected]>
[ Upstream commit adeee7187694719890aaffdc14b7e89cfd736f1d ]
snd_ump_parse_endpoint() sets ump->parsed on every exit, including error, before the caller attaches the legacy rawmidi device. ump_handle_ep_name_msg() then treats parsed as "legacy_rmidi is live" and calls ump_legacy_set_rawmidi_name(), which snprintf()s into ump->legacy_rmidi->name. If a UMP packet arrives in that window (IRQ path from snd_ump_receive), legacy_rmidi is still NULL (KASAN null-ptr-deref in snprintf).
Guard the legacy helpers. parsed only means endpoint info was parsed, not that legacy_rmidi exists.
--- sound/core/ump.c | 4 ++++ 1 file changed, 4 insertions(+)
sound/core/ump.c +4 -0
@@ -1316,6 +1316,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_INPUT );update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); }
@@ -1324,6 +1326,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , ump->core.name); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 605/1102 printk/nbcon: Change nbcon_irq_work to IRQ_WORK_LAZY
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Ogness <[email protected]>
[ Upstream commit 560f4deda32785e260056200f8bb911c475c5b88 ]
Change the nbcon_irq_work to be IRQ_WORK_LAZY, thus not raising an IRQ upon irq_work queuing. The irq_work is then handled on the next kernel tick. This additional delay is acceptable because nbcon_irq_work is only responsible for non-emergency deferred printing, which is delayed anyway. This has the benefit of not needing to raise an IRQ for each printk() call.
On a side note, the Tegra20 and Tegra30 platforms can hang if an irq_work IRQ is raised while entering cpuidle states. This problem was reproducible by calling printk() while entering cpuidle. So this change also provides a workaround for these platforms (as long as they are not running tickless).
--- kernel/printk/nbcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/nbcon.c +1 -1
@@ -1695,7 +1695,7 @@ bool nbcon_alloc(struct console *con)return false;rcuwait_init (&con->rcuwait); -init_irq_work (&con->irq_work, nbcon_irq_work); + con->irq_work =IRQ_WORK_INIT_LAZY (nbcon_irq_work);atomic_long_set (&ACCESS_PRIVATE (con, nbcon_prev_seq),-1UL );nbcon_state_set (con, &state);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 606/1102 ASoC: ux500: Fix MSP stream lifecycle handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit c37ba8fe00f264eee2fd18b0bff7c5f188136c51 ]
The trigger stop path drops the direction busy flag even though ALSA still owns the stream until shutdown. A later trigger cannot reliably restart it, shutdown may leave the block configured, and a second stream may overwrite shared duplex configuration.
Keep configured and running directions as separate state. Program shared settings only for the first direction, require a compatible configuration for the other half of a duplex stream, and enable the frame generator only while a provider stream is running. Also fix the RX-disable direction test and preserve the other direction multichannel setup.
--- sound/soc/ux500/ux500_msp_dai.c | 8 +- sound/soc/ux500/ux500_msp_i2s.c | 183 ++++++++++++++++++++++++-------- sound/soc/ux500/ux500_msp_i2s.h | 4 + 3 files changed, 149 insertions(+), 46 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +6 -2
@@ -34,8 +34,10 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,if (drvdata->slots >1 ) { msp_config->multichannel_configured =1 ; - multi->tx_multichannel_enable = true; - multi->rx_multichannel_enable = true; + multi->tx_multichannel_enable = + msp_config->direction &MSP_DIR_TX ; + multi->rx_multichannel_enable = + msp_config->direction &MSP_DIR_RX ; multi->rx_comparison_enable_mode =MSP_COMPARISON_DISABLED ; multi->tx_channel_0_enable = drvdata->tx_mask;
@@ -192,6 +194,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BC_FC :dev_dbg (dai->dev,"%s: Codec is master.\n" , __func__); + msp_config->clock_provider = false; msp_config->iodelay =0x20 ; msp_config->rx_fsync_sel =0 ; msp_config->tx_fsync_sel =1 <<TFSSEL_SHIFT ;
@@ -204,6 +207,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BP_FP :dev_dbg (dai->dev,"%s: Codec is slave.\n" , __func__); + msp_config->clock_provider = true; msp_config->tx_clk_sel =TX_CLK_SEL_SRG ; msp_config->tx_fsync_sel =TX_SYNC_SRG_PROG ; msp_config->rx_clk_sel =RX_CLK_SEL_SRG ;
sound/soc/ux500/ux500_msp_i2s.c +139 -44
284 lines not on this page · raw message
sound/soc/ux500/ux500_msp_i2s.h +4 -0
@@ -460,6 +460,7 @@ struct ux500_msp_config {enum msp_data_size data_size;unsigned int def_elem_len;unsigned int iodelay; +bool clock_provider; };struct ux500_msp {
@@ -470,8 +471,11 @@ struct ux500_msp {enum msp_state msp_state;int def_elem_len;unsigned int dir_busy; +unsigned int dir_running;int loopback_enable;unsigned int f_bitclk; +bool clock_provider; +struct ux500_msp_config config; }; int ux500_msp_i2s_init_msp(struct platform_device *pdev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 607/1102 ASoC: ux500: Propagate MSP setup errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 3415421a2b0bc4e32bb5a9df24ed7863512d47a7 ]
The prepare callback continues with a partly initialized configuration when format setup fails. Probe likewise tests the allocated pointer instead of the return value, so an MMIO resource or mapping failure can be ignored after allocation succeeds.
Return configuration failures from prepare and test the MSP initialization result directly.
--- sound/soc/ux500/ux500_msp_dai.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +4 -2
@@ -468,7 +468,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,dev_dbg (dai->dev,"%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, dai->id,snd_pcm_stream_str (substream), runtime->rate); -setup_msp_config (substream, dai, &msp_config); + ret =setup_msp_config (substream, dai, &msp_config); +if (ret) +return ret; ret =ux500_msp_i2s_open (drvdata->msp, &msp_config);if (ret <0 ) {
@@ -764,7 +766,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) } ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp); -if (!drvdata->msp) { +if (ret) {dev_err (&pdev->dev,"%s: ERROR: Failed to init MSP-struct (%d)!" , __func__, ret);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 608/1102 ASoC: ux500: Correct MSP frame and bit clock setup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 94c18cea657c48680e4ee20b635b6c01f3eb352e ]
FRPER plus one is the number of bit clocks in a frame. It must follow the configured slot count and width. The legacy rate-dependent constants produce malformed frames; notably, a 16-slot, 16-bit frame is programmed as 278 rather than 256 clocks.
Derive the frame period from the TDM geometry and use the real functional clock rate. Validate that the requested bit clock has an exact, representable divider, program SCKDIV as divider minus one, and report the resulting bit clock using that same divisor.
--- sound/soc/ux500/ux500_msp_dai.c | 75 +++++++-------------------------- sound/soc/ux500/ux500_msp_dai.h | 11 ----- sound/soc/ux500/ux500_msp_i2s.c | 54 ++++++++++++++---------- sound/soc/ux500/ux500_msp_i2s.h | 2 - 4 files changed, 46 insertions(+), 96 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +15 -60
@@ -59,72 +59,21 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,return 0 ; } -static int setup_frameper (struct snd_soc_dai *dai,unsigned int rate, -struct msp_protdesc *prot_desc) +static void setup_frameper (struct snd_soc_dai *dai, +struct msp_protdesc *prot_desc) {struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev); -switch (drvdata->slots) { -case 1 : -switch (rate) { -case 8000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_8_KHZ ; -break ; - -case 16000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_16_KHZ ; -break ; - -case 44100 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_44_1_KHZ ; -break ; - -case 48000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_48_KHZ ; -break ; - -default : -dev_err (dai->dev, -"%s: Error: Unsupported sample-rate (freq = %d)!\n" , - __func__, rate); -return -EINVAL ; - } -break ; - -case 2 : - prot_desc->frame_period =FRAME_PER_2_SLOTS ; -break ; - -case 8 : - prot_desc->frame_period =FRAME_PER_8_SLOTS ; -break ; - -case 16 : - prot_desc->frame_period =FRAME_PER_16_SLOTS ; -break ; -default : -dev_err (dai->dev, -"%s: Error: Unsupported slot-count (slots = %d)!\n" , - __func__, drvdata->slots); -return -EINVAL ; - } - - prot_desc->clocks_per_frame = - prot_desc->frame_period+1 ; + prot_desc->clocks_per_frame = drvdata->slots * drvdata->slot_width; + prot_desc->frame_period = prot_desc->clocks_per_frame -1 ;dev_dbg (dai->dev,"%s: Clocks per frame: %u\n" , __func__, prot_desc->clocks_per_frame); - -return 0 ; } -static int setup_pcm_framing (struct snd_soc_dai *dai,unsigned int rate, -struct msp_protdesc *prot_desc) +static int setup_pcm_framing (struct snd_soc_dai *dai, +struct msp_protdesc *prot_desc) {struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);
@@ -165,7 +114,9 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, prot_desc->tx_elem_len_2 =MSP_ELEM_LEN_16 ; prot_desc->rx_elem_len_2 =MSP_ELEM_LEN_16 ; -return setup_frameper (dai, rate, prot_desc); +setup_frameper (dai, prot_desc); + +return 0 ; } static int setup_clocking(struct snd_soc_dai *dai,
@@ -366,7 +317,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream,if (ret <0 )return ret; - ret =setup_pcm_framing (dai, runtime->rate, prot_desc); + ret =setup_pcm_framing (dai, prot_desc);if (ret <0 )return ret;
@@ -735,7 +686,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) drvdata->tx_mask =0x01 ; drvdata->rx_mask =0x01 ; drvdata->slot_width =16 ; - drvdata->master_clk =MSP_INPUT_FREQ_APB ; drvdata->reg_vape =devm_regulator_get (&pdev->dev,"v-ape" );if (IS_ERR (drvdata->reg_vape)) {
@@ -764,6 +714,11 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret);return ret; } + drvdata->master_clk =clk_get_rate (drvdata->clk); +if (!drvdata->master_clk) { +dev_err (&pdev->dev,"MSP clock has no rate\n" ); +return -EINVAL ; + } ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp);if (ret) {
sound/soc/ux500/ux500_msp_dai.h +0 -11
@@ -22,17 +22,6 @@#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) -#define FRAME_PER_SINGLE_SLOT_8_KHZ 31 -#define FRAME_PER_SINGLE_SLOT_16_KHZ 124 -#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63 -#define FRAME_PER_SINGLE_SLOT_48_KHZ 49 -#define FRAME_PER_2_SLOTS 31 -#define FRAME_PER_8_SLOTS 138 -#define FRAME_PER_16_SLOTS 277 - -#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000 -#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ -#define UX500_MSP_MIN_CHANNELS 1#define UX500_MSP_MAX_CHANNELS 8
sound/soc/ux500/ux500_msp_i2s.c +31 -23
78 lines not on this page · raw message
sound/soc/ux500/ux500_msp_i2s.h +0 -2
@@ -12,8 +12,6 @@#include <linux/platform_device.h> -#define MSP_INPUT_FREQ_APB 48000000 -/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), * 32 bits accesses (stereo). ***/
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 609/1102 ASoC: ux500: Validate MSP DAI configuration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 9ccbacf5a0120964fc1ffacb8151e3347bee9287 ]
Installing channel constraints from hw_params is too late to affect the parameters being committed. The driver consequently accepts channel counts which disagree with the I2S or TDM setup. It also silently truncates out-of-range slot masks and accepts inverted bit clock formats which prepare then rejects.
Validate the selected channel count directly, reject invalid masks before changing cached TDM state, and implement all four standard clock and frame inversion combinations. Use the requested format in validation diagnostics.
--- sound/soc/ux500/ux500_msp_dai.c | 41 ++++++++++++++++++++++----------- sound/soc/ux500/ux500_msp_i2s.c | 7 ++++-- sound/soc/ux500/ux500_msp_i2s.h | 1 + 3 files changed, 33 insertions(+), 16 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +27 -14
@@ -130,7 +130,16 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_NB_IF : msp_config->tx_fsync_pol ^=1 <<TFSPOL_SHIFT ; msp_config->rx_fsync_pol ^=1 <<RFSPOL_SHIFT ; +break ; + +case SND_SOC_DAIFMT_IB_NF : + msp_config->bclk_inverted = true; +break ; +case SND_SOC_DAIFMT_IB_IF : + msp_config->bclk_inverted = true; + msp_config->tx_fsync_pol ^=1 <<TFSPOL_SHIFT ; + msp_config->rx_fsync_pol ^=1 <<RFSPOL_SHIFT ;break ;default :
@@ -453,7 +462,6 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) {unsigned int mask, slots_active; -struct snd_pcm_runtime *runtime = substream->runtime;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
@@ -461,9 +469,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,switch (drvdata->fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S : -snd_pcm_hw_constraint_minmax (runtime, -SNDRV_PCM_HW_PARAM_CHANNELS , -1 ,2 ); +if (params_channels (params) <1 ||params_channels (params) >2 ) +return -EINVAL ;break ;case SND_SOC_DAIFMT_DSP_B :
@@ -475,9 +482,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, slots_active =hweight32 (mask);dev_dbg (dai->dev,"TDM-slots active: %d" , slots_active); -snd_pcm_hw_constraint_single (runtime, -SNDRV_PCM_HW_PARAM_CHANNELS , - slots_active); +if (!slots_active ||params_channels (params) != slots_active) +return -EINVAL ;break ;default :
@@ -510,20 +516,21 @@ static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,default :dev_err (dai->dev,"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n" , - __func__, drvdata->fmt); + __func__, fmt);return -EINVAL ; }switch (fmt &SND_SOC_DAIFMT_INV_MASK ) {case SND_SOC_DAIFMT_NB_NF :case SND_SOC_DAIFMT_NB_IF : +case SND_SOC_DAIFMT_IB_NF :case SND_SOC_DAIFMT_IB_IF :break ;default :dev_err (dai->dev,"%s: Error: Unsupported inversion (fmt = 0x%x)!\n" , - __func__, drvdata->fmt); + __func__, fmt);return -EINVAL ; }
@@ -557,17 +564,23 @@ static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, __func__, slots);return -EINVAL ; } - drvdata->slots = slots; -if (!(slot_width ==16 )) { +if (slot_width !=16 ) {dev_err (dai->dev,"%s: Error: Unsupported slot-width (%d)!\n" , __func__, slot_width);return -EINVAL ; } - drvdata->slot_width = slot_width; - drvdata->tx_mask = tx_mask & cap; - drvdata->rx_mask = rx_mask & cap; +if ((tx_mask | rx_mask) & ~cap) { +dev_err (dai->dev,"%s: Slot mask exceeds %d slots\n" , + __func__, slots); +return -EINVAL ; + } + + drvdata->slots = slots; + drvdata->slot_width = slot_width; + drvdata->tx_mask = tx_mask; + drvdata->rx_mask = rx_mask;return 0 ; }
sound/soc/ux500/ux500_msp_i2s.c +5 -2
@@ -201,10 +201,12 @@ static int configure_protocol(struct ux500_msp *msp,/* The code below should not be separated. */ temp_reg =readl (msp->registers +MSP_GCR ) & ~TX_CLK_POL_RISING ; - temp_reg |=MSP_TX_CLKPOL_BIT (~protdesc->tx_clk_pol); + temp_reg |=MSP_TX_CLKPOL_BIT (!protdesc->tx_clk_pol ^ + config->bclk_inverted);writel (temp_reg, msp->registers +MSP_GCR ); temp_reg =readl (msp->registers +MSP_GCR ) & ~RX_CLK_POL_RISING ; - temp_reg |=MSP_RX_CLKPOL_BIT (protdesc->rx_clk_pol); + temp_reg |=MSP_RX_CLKPOL_BIT (protdesc->rx_clk_pol ^ + config->bclk_inverted);writel (temp_reg, msp->registers +MSP_GCR );return 0 ;
@@ -441,6 +443,7 @@ static bool ux500_msp_config_compatible(struct ux500_msp *msp, active->data_size == config->data_size && active->def_elem_len == config->def_elem_len && active->clock_provider == config->clock_provider && + active->bclk_inverted == config->bclk_inverted && !memcmp (&active->protdesc, &config->protdesc,sizeof (active->protdesc)); }
sound/soc/ux500/ux500_msp_i2s.h +1 -0
@@ -459,6 +459,7 @@ struct ux500_msp_config {unsigned int def_elem_len;unsigned int iodelay;bool clock_provider; +bool bclk_inverted; };struct ux500_msp {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 610/1102 mfd: db8500-prcmu: Remove needless return in three void APIs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zijun Hu <[email protected]>
[ Upstream commit c105c555f8b4fd57b09439806b43b97ebc240ee2 ]
Remove needless 'return' in the following void APIs:
prcmu_early_init()
prcmu_system_reset()
prcmu_modem_reset()
Since both the API and callee involved are void functions.
Stable-dep-of: 66ec63e7a90b ("ASoC: ux500: Deassert the MSP reset during probe")
--- include/linux/mfd/dbx500-prcmu.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
include/linux/mfd/dbx500-prcmu.h +3 -3
@@ -215,7 +215,7 @@ struct prcmu_fw_version {static inline void prcmu_early_init (void ) { -return db8500_prcmu_early_init (); +db8500_prcmu_early_init (); } static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
@@ -302,7 +302,7 @@ static inline int prcmu_request_ape_opp_100_voltage(bool enable)static inline void prcmu_system_reset (u16 reset_code) { -return db8500_prcmu_system_reset (reset_code); +db8500_prcmu_system_reset (reset_code); }static inline u16 prcmu_get_reset_code (void )
@@ -314,7 +314,7 @@ int prcmu_ac_wake_req(void);void prcmu_ac_sleep_req (void );static inline void prcmu_modem_reset (void ) { -return db8500_prcmu_modem_reset (); +db8500_prcmu_modem_reset (); }static inline bool prcmu_is_ac_wake_requested (void )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 611/1102 arm: Handle KCOV __init vs inline mismatches
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kees Cook <[email protected]>
[ Upstream commit 2424fe1cac4fc8ea0520ba22ede7544c3ddc8dd1 ]
When KCOV is enabled all functions get instrumented, unless the __no_sanitize_coverage attribute is used. To prepare for __no_sanitize_coverage being applied to __init functions, we have to handle differences in how GCC's inline optimizations get resolved. For arm this exposed several places where __init annotations were missing but ended up being "accidentally correct". Fix these cases and force several functions to be inline with __always_inline.
Stable-dep-of: 66ec63e7a90b ("ASoC: ux500: Deassert the MSP reset during probe")
--- arch/arm/mm/cache-feroceon-l2.c | 2 +- arch/arm/mm/cache-tauros2.c | 2 +- drivers/clocksource/timer-orion.c | 2 +- drivers/soc/ti/pm33xx.c | 2 +- include/linux/mfd/dbx500-prcmu.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-)
arch/arm/mm/cache-feroceon-l2.c +1 -1
@@ -295,7 +295,7 @@ static inline u32 read_extra_features(void)return u; } -static inline void write_extra_features (u32 u) +static inline void __init write_extra_features (u32 u) { __asm__("mcr p15, 1, %0, c15, c1, 0" : :"r" (u)); }
arch/arm/mm/cache-tauros2.c +1 -1
@@ -177,7 +177,7 @@ static inline void __init write_actlr(u32 actlr) __asm__("mcr p15, 0, %0, c1, c0, 1\n" : :"r" (actlr)); } -static void enable_extra_feature (unsigned int features) +static void __init enable_extra_feature (unsigned int features) {u32 u;
drivers/clocksource/timer-orion.c +1 -1
@@ -43,7 +43,7 @@ static struct delay_timer orion_delay_timer = { .read_current_timer = orion_read_timer, }; -static void orion_delay_timer_init (unsigned long rate) +static void __init orion_delay_timer_init (unsigned long rate) { orion_delay_timer.freq = rate;register_current_timer_delay (&orion_delay_timer);
drivers/soc/ti/pm33xx.c +1 -1
@@ -145,7 +145,7 @@ static int am33xx_do_sram_idle(u32 wfi_flags)return pm_ops->cpu_suspend (am33xx_do_wfi_sram, wfi_flags); } -static int __init am43xx_map_gic (void ) +static int am43xx_map_gic (void ) { gic_dist_base =ioremap (AM43XX_GIC_DIST_BASE ,SZ_4K );
include/linux/mfd/dbx500-prcmu.h +1 -1
@@ -213,7 +213,7 @@ struct prcmu_fw_version {#if defined(CONFIG_UX500_SOC_DB8500 ) -static inline void prcmu_early_init (void ) +static inline void __init prcmu_early_init (void ) {db8500_prcmu_early_init (); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 612/1102 mfd: db8500-prcmu: Fold dbx500 header into db8500
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit b8bc38bcecb77880a802d0430862b023c0aa7392 ]
Move the DBx500 PRCMU definitions into the DB8500 PRCMU header and delete the wrapper header.
Convert users of simple PRCMU wrappers to call the DB8500 helpers directly.
The dbx500-prcmu.h header was the result of an earlier attempt to abstract several DBx5x SoC PRCMU units to use the same abstract header. They are deleted from the kernel and this is not just causing maintenance burden and build errors.
The stub code is using -ENOSYS in a way checkpatch complains about so replace these with -EINVAL while we're at it.
Stable-dep-of: 66ec63e7a90b ("ASoC: ux500: Deassert the MSP reset during probe")
--- arch/arm/mach-ux500/cpu-db8500.c | 6 +- drivers/clk/ux500/clk-prcmu.c | 20 +- drivers/clk/ux500/u8500_of_clk.c | 2 +- drivers/cpuidle/cpuidle-ux500.c | 6 +- drivers/mfd/ab8500-core.c | 2 +- drivers/mfd/db8500-prcmu.c | 6 +- drivers/regulator/db8500-prcmu.c | 12 +- drivers/thermal/db8500_thermal.c | 10 +- drivers/watchdog/db8500_wdt.c | 22 +- include/linux/mfd/db8500-prcmu.h | 252 +++++++++++++- include/linux/mfd/dbx500-prcmu.h | 575 ------------------------------- sound/soc/ux500/ux500_msp_dai.c | 2 +- 12 files changed, 294 insertions(+), 621 deletions(-) delete mode 100644 include/linux/mfd/dbx500-prcmu.h
arch/arm/mach-ux500/cpu-db8500.c +3 -3
@@ -12,7 +12,7 @@#include <linux/irq.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/platform_data/arm-ux500-pm.h> #include <linux/platform_device.h> #include <linux/io.h>
@@ -81,7 +81,7 @@ static void __init ux500_init_irq(void)struct resource r;irqchip_init (); -prcmu_early_init (); +db8500_prcmu_early_init (); np =of_find_compatible_node (NULL ,NULL ,"stericsson,db8500-prcmu" );of_address_to_resource (np,0 , &r);of_node_put (np);
@@ -101,7 +101,7 @@ static void ux500_restart(enum reboot_mode mode, const char *cmd)local_irq_disable ();local_fiq_disable (); -prcmu_system_reset (0 ); +db8500_prcmu_system_reset (0 ); }static const struct of_device_id u8500_local_bus_nodes[] = {
drivers/clk/ux500/clk-prcmu.c +10 -10
@@ -7,7 +7,7 @@*/ #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h>
@@ -35,13 +35,13 @@ static int clk_prcmu_prepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -return prcmu_request_clock (clk->cg_sel, true); +return db8500_prcmu_request_clock (clk->cg_sel, true); }static void clk_prcmu_unprepare (struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) +if (db8500_prcmu_request_clock (clk->cg_sel, false))pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw)); }
@@ -84,7 +84,7 @@ static int clk_prcmu_opp_prepare(struct clk_hw *hw) clk->opp_requested =1 ; } - err =prcmu_request_clock (clk->cg_sel, true); + err =db8500_prcmu_request_clock (clk->cg_sel, true);if (err) {prcmu_qos_remove_requirement (PRCMU_QOS_APE_OPP , (char *)clk_hw_get_name (hw));
@@ -99,7 +99,7 @@ static void clk_prcmu_opp_unprepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) { +if (db8500_prcmu_request_clock (clk->cg_sel, false)) {pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw));return ;
@@ -118,7 +118,7 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)struct clk_prcmu *clk =to_clk_prcmu (hw);if (!clk->opp_requested) { - err =prcmu_request_ape_opp_100_voltage (true); + err =db8500_prcmu_request_ape_opp_100_voltage (true);if (err) {pr_err ("clk_prcmu: %s fail req APE OPP VOLT for %s.\n" , __func__,clk_hw_get_name (hw));
@@ -127,9 +127,9 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) clk->opp_requested =1 ; } - err =prcmu_request_clock (clk->cg_sel, true); + err =db8500_prcmu_request_clock (clk->cg_sel, true);if (err) { -prcmu_request_ape_opp_100_voltage (false); +db8500_prcmu_request_ape_opp_100_voltage (false); clk->opp_requested =0 ;return err; }
@@ -141,14 +141,14 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) { +if (db8500_prcmu_request_clock (clk->cg_sel, false)) {pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw));return ; }if (clk->opp_requested) { -prcmu_request_ape_opp_100_voltage (false); +db8500_prcmu_request_ape_opp_100_voltage (false); clk->opp_requested =0 ; } }
drivers/clk/ux500/u8500_of_clk.c +1 -1
@@ -9,7 +9,7 @@#include <linux/of.h> #include <linux/of_address.h> #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include "clk.h" #include "prcc.h"
drivers/cpuidle/cpuidle-ux500.c +3 -3
@@ -11,7 +11,7 @@#include <linux/spinlock.h> #include <linux/atomic.h> #include <linux/smp.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/platform_data/arm-ux500-pm.h> #include <linux/platform_device.h>
@@ -66,7 +66,7 @@ static inline int ux500_enter_idle(struct cpuidle_device *dev,/* Go to the retention state, the prcmu will wait for the * cpu to go WFI and this is what happens after exiting this * 'master' critical section */ -if (prcmu_set_power_state (PRCMU_AP_IDLE , true, true)) +if (db8500_prcmu_set_power_state (PRCMU_AP_IDLE , true, true)) goto out;/* When we switch to retention, the prcmu is in charge
@@ -109,7 +109,7 @@ static struct cpuidle_driver ux500_idle_driver = {static int dbx500_cpuidle_probe (struct platform_device *pdev) {/* Configure wake up reasons */ -prcmu_enable_wakeups (PRCMU_WAKEUP (ARM ) |PRCMU_WAKEUP (RTC ) | +db8500_prcmu_enable_wakeups (PRCMU_WAKEUP (ARM ) |PRCMU_WAKEUP (RTC ) |PRCMU_WAKEUP (ABB ));return cpuidle_register (&ux500_idle_driver,NULL );
drivers/mfd/ab8500-core.c +1 -1
@@ -19,7 +19,7 @@#include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/of.h> /*
drivers/mfd/db8500-prcmu.c +3 -3
@@ -32,7 +32,7 @@#include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/mfd/core.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h>
@@ -2285,7 +2285,7 @@ void db8500_prcmu_system_reset(u16 reset_code)/** * db8500_prcmu_get_reset_code - Retrieve SW reset reason code * -* Retrieves the reset reason code stored by prcmu_system_reset() before +* Retrieves the reset reason code stored by db8500_prcmu_system_reset() before * last restart. */ u16 db8500_prcmu_get_reset_code (void )
@@ -3041,7 +3041,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)db8500_irq_init (np); -prcmu_config_esram0_deep_sleep (ESRAM0_DEEP_SLEEP_STATE_RET ); +db8500_prcmu_config_esram0_deep_sleep (ESRAM0_DEEP_SLEEP_STATE_RET ); err =mfd_add_devices (&pdev->dev,0 , common_prcmu_devs,ARRAY_SIZE (common_prcmu_devs),NULL ,0 , db8500_irq_domain);
drivers/regulator/db8500-prcmu.c +6 -6
@@ -13,7 +13,7 @@#include <linux/err.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/db8500-prcmu.h>
@@ -93,13 +93,13 @@ static int enable_epod(u16 epod_id, bool ramret)if (ramret) {if (!epod_on[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_RAMRET ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_RAMRET );if (ret <0 )return ret; } epod_ramret[epod_id] = true; }else { - ret =prcmu_set_epod (epod_id,EPOD_STATE_ON ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_ON );if (ret <0 )return ret; epod_on[epod_id] = true;
@@ -114,18 +114,18 @@ static int disable_epod(u16 epod_id, bool ramret)if (ramret) {if (!epod_on[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_OFF ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_OFF );if (ret <0 )return ret; } epod_ramret[epod_id] = false; }else {if (epod_ramret[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_RAMRET ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_RAMRET );if (ret <0 )return ret; }else { - ret =prcmu_set_epod (epod_id,EPOD_STATE_OFF ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_OFF );if (ret <0 )return ret; }
drivers/thermal/db8500_thermal.c +5 -5
@@ -10,7 +10,7 @@#include <linux/cpu_cooling.h> #include <linux/interrupt.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h>
@@ -82,7 +82,7 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th,unsigned long next_low, unsigned long next_high) { -prcmu_stop_temp_sense (); +db8500_prcmu_stop_temp_sense (); th->cur_index = idx; th->interpolated_temp = (next_low + next_high)/2 ;
@@ -91,8 +91,8 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th,* The PRCMU accept absolute temperatures in celsius so divide * down the millicelsius with 1000 */ -prcmu_config_hotmon ((u8)(next_low/1000 ), (u8)(next_high/1000 )); -prcmu_start_temp_sense (PRCMU_DEFAULT_MEASURE_TIME ); +db8500_prcmu_config_hotmon ((u8)(next_low /1000 ), (u8)(next_high /1000 )); +db8500_prcmu_start_temp_sense (PRCMU_DEFAULT_MEASURE_TIME ); }static irqreturn_t prcmu_low_irq_handler (int irq,void *irq_data)
@@ -204,7 +204,7 @@ static int db8500_thermal_probe(struct platform_device *pdev)static int db8500_thermal_suspend (struct platform_device *pdev,pm_message_t state) { -prcmu_stop_temp_sense (); +db8500_prcmu_stop_temp_sense ();return 0 ; }
drivers/watchdog/db8500_wdt.c +11 -11
@@ -16,7 +16,7 @@#include <linux/watchdog.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #define WATCHDOG_TIMEOUT 600/* 10 minutes */
@@ -37,24 +37,24 @@ MODULE_PARM_DESC(nowayout,static int db8500_wdt_start (struct watchdog_device *wdd) { -return prcmu_enable_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_enable_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_stop (struct watchdog_device *wdd) { -return prcmu_disable_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_disable_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_keepalive (struct watchdog_device *wdd) { -return prcmu_kick_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_kick_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_set_timeout (struct watchdog_device *wdd,unsigned int timeout) {db8500_wdt_stop (wdd); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (wdd);return 0 ;
@@ -91,10 +91,10 @@ static int db8500_wdt_probe(struct platform_device *pdev)watchdog_set_nowayout (&db8500_wdt, nowayout);/* disable auto off on sleep */ -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false);/* set HW initial value */ -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); ret =devm_watchdog_register_device (dev, &db8500_wdt);if (ret)
@@ -110,9 +110,9 @@ static int db8500_wdt_suspend(struct platform_device *pdev, {if (watchdog_active (&db8500_wdt)) {db8500_wdt_stop (&db8500_wdt); -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , true); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , true); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (&db8500_wdt); }return 0 ;
@@ -122,9 +122,9 @@ static int db8500_wdt_resume(struct platform_device *pdev) {if (watchdog_active (&db8500_wdt)) {db8500_wdt_stop (&db8500_wdt); -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (&db8500_wdt); }return 0 ;
include/linux/mfd/db8500-prcmu.h +250 -2
312 lines not on this page · raw message
include/linux/mfd/dbx500-prcmu.h deleted +0 -575
575 lines not on this page · raw message
sound/soc/ux500/ux500_msp_dai.c +1 -1
@@ -14,7 +14,7 @@#include <linux/clk.h> #include <linux/of.h> #include <linux/regulator/consumer.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h> #include <sound/soc-dai.h>
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 613/1102 ASoC: ux500: Deassert the MSP reset during probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 66ec63e7a90bedc56aa050fbe437964008c3d584 ]
The devicetree has described each MSP reset line since the PRCC reset controller was added, but the driver never acquires or deasserts it. The block can consequently remain inaccessible when firmware has left it in reset.
Acquire the reset exclusively and keep it deasserted for the lifetime of the bound device.
--- sound/soc/ux500/ux500_msp_dai.c | 7 +++++++ 1 file changed, 7 insertions(+)
sound/soc/ux500/ux500_msp_dai.c +7 -0
@@ -14,6 +14,7 @@#include <linux/clk.h> #include <linux/of.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h>
@@ -686,6 +687,7 @@ static const struct snd_soc_component_driver ux500_msp_component = {static int ux500_msp_drv_probe (struct platform_device *pdev) {struct ux500_msp_i2s_drvdata *drvdata; +struct reset_control *reset;int ret =0 ; drvdata = devm_kzalloc(&pdev->dev,
@@ -733,6 +735,11 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)return -EINVAL ; } + reset =devm_reset_control_get_exclusive_deasserted (&pdev->dev,NULL ); +if (IS_ERR (reset)) +return dev_err_probe (&pdev->dev,PTR_ERR (reset), +"Failed to deassert MSP reset\n" ); + ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp);if (ret) { dev_err(&pdev->dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 614/1102 ASoC: ux500: Request the MSP MMIO resource
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 4fb67925f33ad789e9e00903a73306ed40f7ae32 ]
A bare devm_ioremap() neither reserves the register range nor preserves the platform resource error. This permits another driver to claim the same range and reports every mapping failure as an allocation failure.
Use the managed platform resource helper, retaining the resolved resource only to derive the DMA register address.
--- sound/soc/ux500/ux500_msp_i2s.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
sound/soc/ux500/ux500_msp_i2s.c +4 -14
@@ -733,7 +733,7 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)int ux500_msp_i2s_init_msp (struct platform_device *pdev,struct ux500_msp **msp_p) { -struct resource *res =NULL ; +struct resource *res;struct ux500_msp *msp; *msp_p =devm_kzalloc (&pdev->dev,sizeof (struct ux500_msp ),GFP_KERNEL );
@@ -743,20 +743,10 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, msp->dev = &pdev->dev; - res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); -if (res ==NULL ) { -dev_err (&pdev->dev,"%s: ERROR: Unable to get resource!\n" , - __func__); -return -ENOMEM ; - } - + msp->registers =devm_platform_get_and_ioremap_resource (pdev,0 , &res); +if (IS_ERR (msp->registers)) +return PTR_ERR (msp->registers); msp->tx_rx_addr = res->start +MSP_DR ; - msp->registers =devm_ioremap (&pdev->dev, res->start, -resource_size (res)); -if (msp->registers ==NULL ) { -dev_err (&pdev->dev,"%s: ERROR: ioremap failed!\n" , __func__); -return -ENOMEM ; - } msp->msp_state =MSP_STATE_IDLE ; msp->loopback_enable =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 615/1102 ASoC: ux500: Remove obsolete PRCMU QoS calls
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 7b819677b503667422b0b7bdb21853e0066f8606 ]
The DB8500 PRCMU QoS interface consists of unconditional inline stubs, so the MSP calls and cached constraint state have no effect. Device power and clocks are already represented by the regulator, power-domain and common-clock frameworks.
Remove the dead calls and their private state instead of pretending to change the APE operating point.
Stable-dep-of: dc1a1b1e2206 ("ASoC: ux500: Allow repeated MSP prepare calls")
--- sound/soc/ux500/ux500_msp_dai.c | 26 -------------------------- sound/soc/ux500/ux500_msp_dai.h | 2 -- 2 files changed, 28 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +0 -26
@@ -15,7 +15,6 @@#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> -#include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h> #include <sound/soc-dai.h>
@@ -393,12 +392,6 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,dev_dbg (dai->dev,"%s: MSP %d (%s): Enter.\n" , __func__, dai->id,snd_pcm_stream_str (substream)); -if (drvdata->vape_opp_constraint ==1 ) { -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500_msp_i2s" ,50 ); - drvdata->vape_opp_constraint =0 ; - } -if (ux500_msp_i2s_close (drvdata->msp, is_playback ?MSP_DIR_TX :MSP_DIR_RX )) { dev_err(dai->dev,
@@ -440,21 +433,6 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,return ret; } -/* Set OPP-level */ -if ((drvdata->fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) && - (drvdata->msp->f_bitclk >19200000 )) { -/* If the bit-clock is higher than 19.2MHz, Vape should be -* run in 100% OPP. Only when bit-clock is used (MSP master) -*/ -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500-msp-i2s" ,100 ); - drvdata->vape_opp_constraint =1 ; - }else { -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500-msp-i2s" ,50 ); - drvdata->vape_opp_constraint =0 ; - } -return ret; }
@@ -710,8 +688,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret);return ret; } -prcmu_qos_add_requirement (PRCMU_QOS_APE_OPP , (char *)pdev->name,50 ); - drvdata->pclk =devm_clk_get (&pdev->dev,"apb_pclk" );if (IS_ERR (drvdata->pclk)) { ret = (int )PTR_ERR (drvdata->pclk);
@@ -780,8 +756,6 @@ static void ux500_msp_drv_remove(struct platform_device *pdev)snd_soc_unregister_component (&pdev->dev); -prcmu_qos_remove_requirement (PRCMU_QOS_APE_OPP ,"ux500_msp_i2s" ); -ux500_msp_i2s_cleanup_msp (pdev, drvdata->msp); }
sound/soc/ux500/ux500_msp_dai.h +0 -2
@@ -46,8 +46,6 @@ struct ux500_msp_i2s_drvdata {struct clk *clk;struct clk *pclk; -/* Regulators */ -int vape_opp_constraint; };int ux500_msp_dai_set_data_delay (struct snd_soc_dai *dai,int delay);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 616/1102 ASoC: ux500: Allow repeated MSP prepare calls
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit dc1a1b1e22066f01bb86a9b11ee998d4dc72db66 ]
ALSA can call the DAI prepare callback again after an XRUN without first shutting down the stream. The MSP open helper rejects the second call with -EBUSY because the direction remains configured.
Track successful playback and capture configurations at the DAI layer. Make repeated prepare calls no-ops and only close directions which were successfully prepared.
--- sound/soc/ux500/ux500_msp_dai.c | 28 +++++++++++++++++++++------- sound/soc/ux500/ux500_msp_dai.h | 1 + 2 files changed, 22 insertions(+), 7 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +21 -7
@@ -388,15 +388,21 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,int ret;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);bool is_playback = (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ); +unsigned int configured = is_playback ?PLAYBACK_CONFIGURED : +CAPTURE_CONFIGURED ; +unsigned int dir = is_playback ?MSP_DIR_TX :MSP_DIR_RX ;dev_dbg (dai->dev,"%s: MSP %d (%s): Enter.\n" , __func__, dai->id,snd_pcm_stream_str (substream)); -if (ux500_msp_i2s_close (drvdata->msp, - is_playback ?MSP_DIR_TX :MSP_DIR_RX )) { -dev_err (dai->dev, -"%s: Error: MSP %d (%s): Unable to close i2s.\n" , - __func__, dai->id,snd_pcm_stream_str (substream)); +if (drvdata->configured & configured) { +if (ux500_msp_i2s_close (drvdata->msp, dir)) { +dev_err (dai->dev, +"%s: Error: MSP %d (%s): Unable to close i2s.\n" , + __func__, dai->id, +snd_pcm_stream_str (substream)); + } + drvdata->configured &= ~configured; }/* Disable and unprepare clocks */
@@ -414,14 +420,20 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,static int ux500_msp_dai_prepare (struct snd_pcm_substream *substream,struct snd_soc_dai *dai) { -int ret =0 ;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);struct snd_pcm_runtime *runtime = substream->runtime;struct ux500_msp_config msp_config; +bool is_playback = substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ; +unsigned int configured = is_playback ?PLAYBACK_CONFIGURED : +CAPTURE_CONFIGURED ; +int ret;dev_dbg (dai->dev,"%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, dai->id,snd_pcm_stream_str (substream), runtime->rate); +if (drvdata->configured & configured) +return 0 ; + ret =setup_msp_config (substream, dai, &msp_config);if (ret)return ret;
@@ -433,7 +445,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,return ret; } -return ret; + drvdata->configured |= configured; + +return 0 ; } static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
sound/soc/ux500/ux500_msp_dai.h +1 -0
@@ -36,6 +36,7 @@ struct ux500_msp_i2s_drvdata {struct ux500_msp *msp;struct regulator *reg_vape;unsigned int fmt; +unsigned int configured;unsigned int tx_mask;unsigned int rx_mask;int slots;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 617/1102 ASoC: ux500: Program the MSP FIFO watermarks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 2519439b4b5f6ee95879b1a44fc373127291b1e4 ]
The DMA engine is configured for four-element bursts, but the MSP driver never programs the FIFO watermark register and instead depends on its previous or reset value. The DB8500 DMA request protocol requires the peripheral watermark to match the DMA packet size.
Program four-element receive and transmit watermarks when configuring the first direction, before enabling MSP DMA requests.
--- sound/soc/ux500/ux500_msp_i2s.c | 2 ++ sound/soc/ux500/ux500_msp_i2s.h | 5 +++++ 2 files changed, 7 insertions(+)
sound/soc/ux500/ux500_msp_i2s.c +2 -0
@@ -507,6 +507,8 @@ int ux500_msp_i2s_open(struct ux500_msp *msp, old_reg &= ~mask; new_reg |= old_reg;writel (new_reg, msp->registers +MSP_GCR ); +writel (MSP_WMRK_TX_4_ELEMENTS |MSP_WMRK_RX_4_ELEMENTS , + msp->registers +MSP_WMRK ); } res =enable_msp (msp, config, first);
sound/soc/ux500/ux500_msp_i2s.h +5 -0
@@ -62,6 +62,7 @@ enum msp_direction {#define MSP_SRG 0x10#define MSP_FLR 0x14#define MSP_DMACR 0x18 +#define MSP_WMRK 0x1c#define MSP_IMSC 0x20#define MSP_RIS 0x24
@@ -228,6 +229,10 @@ enum msp_direction {#define RDMAE_SHIFT 0#define TDMAE_SHIFT 1 +/* FIFO watermark register */ +#define MSP_WMRK_RX_4_ELEMENTS BIT(0) +#define MSP_WMRK_TX_4_ELEMENTS BIT(3) +/* Interrupt Register */ #define RX_SERVICE_INT BIT(0)#define RX_OVERRUN_ERROR_INT BIT(1)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 618/1102 btrfs: fix transaction use-after-free in raid stripe insertion
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
[ Upstream commit a8813a923f9e43f788b357fb55c35f7f6ed6f98c ]
If allocation of a RAID stripe extent fails, btrfs_insert_one_raid_extent() aborts and ends the transaction before returning -ENOMEM.
btrfs_finish_one_ordered(), the production caller through btrfs_insert_raid_extent(), still owns the transaction handle. It handles the error by aborting the transaction and then reaches the common exit path, which ends the transaction again.
The premature end can free the handle and drop its transaction reference. Transaction cleanup can then free the transaction before the caller's second abort accesses the handle and transaction, resulting in use-after-free.
Keep the abort at the failure site, but let the caller's common exit path end the transaction once, after it has finished using both objects.
--- fs/btrfs/raid-stripe-tree.c | 1 - 1 file changed, 1 deletion(-)
fs/btrfs/raid-stripe-tree.c +0 -1
@@ -122,7 +122,6 @@ static int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, stripe_extent =kzalloc (item_size,GFP_NOFS );if (!stripe_extent) {btrfs_abort_transaction (trans, -ENOMEM ); -btrfs_end_transaction (trans);return -ENOMEM ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 619/1102 btrfs: fix the possible bioc_list memory leak during error
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit afbe73778338e6d1ac8c4486fbdf33f0cc1f2624 ]
There are two possible ways to leak bioc memory on btrfs_ordered_extent::bioc_list:
- An error occurred for btrfs_insert_one_raid_extent() Then the function btrfs_insert_raid_extent() immediately return without freeing any bioc in the bioc_list.
- An ordered extent hit an IO error
In that case the ordered extent will have BTRFS_ORDERED_IOERR set, and skip the call on btrfs_insert_raid_extent() completely.
Fix the problem by:
- Introduce a new helper, btrfs_cleanup_ordered_bioc_list() Which will remove all bioc from the bioc_list, and release the bioc.
- Call the above helper for btrfs_insert_raid_extent() So that the cleanup helper is always called no matter what.
- Call the above helper for btrfs_finish_one_ordered() This is called just before the final release on the ordered extent.
This was reported by Sashiko when reviewing another patch.
--- fs/btrfs/inode.c | 3 +++ fs/btrfs/raid-stripe-tree.c | 18 ++++++++++++------ fs/btrfs/raid-stripe-tree.h | 1 + 3 files changed, 16 insertions(+), 6 deletions(-)
fs/btrfs/inode.c +3 -0
@@ -3254,6 +3254,9 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent)*/ btrfs_remove_ordered_extent (inode, ordered_extent); +/* Cleanup any remaining biocs attached to the OE. */ +btrfs_cleanup_ordered_bioc_list (ordered_extent); +/* once for us */ btrfs_put_ordered_extent (ordered_extent);/* once for the tree */
fs/btrfs/raid-stripe-tree.c +12 -6
@@ -161,7 +161,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, struct btrfs_ordered_extent *ordered_extent) {struct btrfs_io_context *bioc; -int ret; +int ret =0 ;if (!btrfs_fs_incompat (trans->fs_info,RAID_STRIPE_TREE ))return 0 ;
@@ -169,17 +169,23 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans,list_for_each_entry (bioc, &ordered_extent->bioc_list, rst_ordered_entry) { ret =btrfs_insert_one_raid_extent (trans, bioc);if (ret) -return ret; +break ; } -while (!list_empty (&ordered_extent->bioc_list)) { - bioc =list_first_entry (&ordered_extent->bioc_list, +btrfs_cleanup_ordered_bioc_list (ordered_extent); +return ret; +} + +void btrfs_cleanup_ordered_bioc_list (struct btrfs_ordered_extent *ordered) +{ +while (!list_empty (&ordered->bioc_list)) { +struct btrfs_io_context *bioc; + + bioc =list_first_entry (&ordered->bioc_list,typeof (*bioc), rst_ordered_entry);list_del (&bioc->rst_ordered_entry);btrfs_put_bioc (bioc); } - -return 0 ; } int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info,
fs/btrfs/raid-stripe-tree.h +1 -0
@@ -27,6 +27,7 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, u32 stripe_index, struct btrfs_io_stripe *stripe);int btrfs_insert_raid_extent (struct btrfs_trans_handle *trans,struct btrfs_ordered_extent *ordered_extent); +void btrfs_cleanup_ordered_bioc_list (struct btrfs_ordered_extent *ordered);static inline bool btrfs_need_stripe_tree_update (struct btrfs_fs_info *fs_info,u64 map_type)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 620/1102 btrfs: return proper negative error code for update_raid_extent_item()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit a03fa65184545837d6461413275da71f30527385 ]
The function btrfs_abort_transaction() only accepts negative error code, and have the macro VERIFY_NEGATIVE_ERROR() to verify that error code.
But inside update_raid_extent_item(), if there is such key found, we return 1, breaking the negative error code scheme.
Furthermore if we hit some real error during the tree search, e.g. -EIO, then the error code is always over-written to -EINVAL.
Fix both problems by following other call sites by overwriting @ret to -ENOENT if the btrfs_search_slot() failed to locate the key.
This is very unlikely to hit, as we only enter update_raid_extent_item() if there is a conflicting key already in the raid stripe tree.
This was reported by Sashiko when reviewing another patch.
--- fs/btrfs/raid-stripe-tree.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
fs/btrfs/raid-stripe-tree.c +4 -2
@@ -94,8 +94,10 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans, ret =btrfs_search_slot (trans, trans->fs_info->stripe_root, key, path,0 ,1 ); -if (ret) -return (ret ==1 ? ret : -EINVAL ); +if (ret >0 ) + ret = -ENOENT ; +if (ret <0 ) +return ret; leaf = path->nodes[0 ]; slot = path->slots[0 ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 621/1102 btrfs: zoned: finish active block group cleanup if call_zone_finish() fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johannes Thumshirn <[email protected]>
[ Upstream commit a18a6b93a2843b9d103d3456bbd4b3f90282a379 ]
do_zone_finish() clears BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE before finishing the zones. If call_zone_finish() then fails it returned early, leaving the now inactive block group on fs_info->zone_active_bgs, leaking its reference, the BTRFS_FS_NEED_ZONE_FINISH waiters are never woken, and as its alloc_offset equals the zone capacity btrfs_zone_finish_one_bg() keeps selecting it, spinning btrfs_zoned_activate_one_bg().
Fall through to the cleanup on failure too and return the error, but keep the block group read-only as its zones are left inconsistent.
--- fs/btrfs/zoned.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
fs/btrfs/zoned.c +4 -7
@@ -2559,16 +2559,13 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writdown_read (&dev_replace->rwsem); map = block_group->physical_map;for (i =0 ; i < map->num_stripes; i++) { - ret =call_zone_finish (block_group, &map->stripes[i]); -if (ret) { -up_read (&dev_replace->rwsem); -return ret; - } +if (ret) +break ; }up_read (&dev_replace->rwsem); -if (!fully_written) +if (!ret && !fully_written)btrfs_dec_block_group_ro (block_group);spin_lock (&fs_info->zone_active_bgs_lock);
@@ -2581,7 +2578,7 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writclear_and_wake_up_bit (BTRFS_FS_NEED_ZONE_FINISH , &fs_info->flags); -return 0 ; +return ret; }int btrfs_zone_finish (struct btrfs_block_group *block_group)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 622/1102 btrfs: send: fix lost error return value in will_overwrite_ref()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
[ Upstream commit d0285dfbc3b46f41395b26ee2f4a16d99fb3e736 ]
The direct-return refactoring in commit b3047a42f55d ("btrfs: send:
directly return from will_overwrite_ref() and simplify it") changed will_overwrite_ref() to return directly instead of going through the common out label.
That resulted in a negative return value from is_inode_existent() to start being converted to 0, making lookup errors unable to be distinguished from the inode not existing.
process_recorded_refs() expects negative errors from will_overwrite_ref() and aborts processing when it receives one.
Return the value from is_inode_existent() to restore the previous error propagation behavior as it was before the refactor.
--- fs/btrfs/send.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/btrfs/send.c +1 -1
@@ -2157,7 +2157,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, ret =is_inode_existent (sctx, dir, dir_gen,NULL , &parent_root_dir_gen);if (ret <=0 ) -return 0 ; +return ret;/* * If we have a parent root we need to verify that the parent dir was
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 623/1102 btrfs: do not force reloc root creation during qgroup_account_snapshot()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit cacf35832292997018837e484283f95a9301ebf5 ]
[BUG]
When running btrfs/252 with quota enabled through MKFS_OPTIONS="-O quota", it has a high chance to trigger the following kernel warning and flips the fs RO:
BTRFS info (device dm-2): relocating block group 30408704 flags metadata|dup
------------[ cut here ]------------
WARNING: fs/btrfs/extent-tree.c:879 at lookup_inline_extent_backref+0x74b/0x960 [btrfs], CPU#4: btrfs/2173
CPU: 4 UID: 0 PID: 2173 Comm: btrfs Not tainted 7.2.0-rc6-custom+ #457 PREEMPT(full) 3adc6528fb66f7a55fe1095385818e742f200aab
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS unknown 02/02/2022
RIP: 0010:lookup_inline_extent_backref+0x74b/0x960 [btrfs]
Call Trace:
<TASK>
insert_inline_extent_backref+0x7c/0x160 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__btrfs_inc_extent_ref+0xa9/0x270 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__btrfs_run_delayed_refs+0x4af/0x11c0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_run_delayed_refs+0x9d/0xf0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
create_pending_snapshot+0x39d/0xf00 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
create_pending_snapshots+0x9b/0xc0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_commit_transaction+0x280/0xeb0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
prepare_to_relocate+0x147/0x200 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
relocate_block_group+0x6b/0x5e0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_relocate_block_group+0x92c/0x2380 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_relocate_chunk+0x3f/0x1a0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_balance+0xa2c/0x19c0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_ioctl+0x2839/0x2d30 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__x64_sys_ioctl+0x416/0x9a0
do_syscall_64+0xe1/0x790
entry_SYSCALL_64_after_hwframe+0x4b/0x53
</TASK>
---[ end trace 0000000000000000 ]---
BTRFS info (device dm-2): leaf 4593991680 gen 233 total ptrs 175 free space 5953 owner 2
BTRFS info (device dm-2): refs 3 lock_owner 2173 current 2173
item 0 key (166772736 METADATA_ITEM 1) itemoff 16250 itemsize 33
extent refs 1 gen 222 flags 2
ref#0: tree block backref root 266
[ Skip the tree dump ]
item 174 key (263225344 METADATA_ITEM 0) itemoff 10328 itemsize 33
extent refs 1 gen 162 flags 258
ref#0: tree block backref root 267
BTRFS error (device dm-2): extent item not found for insert, bytenr 179847168 num_bytes 16384 parent 4594335744 root_objectid 273 owner 0 offset 0
BTRFS error (device dm-2): failed to run delayed ref for logical 179847168 num_bytes 16384 type 182 action 1 ref_mod 1: -117[CAUSE]
The above error is showing that there is a tree reference to a metadata extent that is no longer there.
With "ref_verify" mount option (requires CONFIG_BTRFS_DEBUG), there is some extra debug output:
BTRFS error (device dm-2): dumping block entry [180961280 16384], num_refs 0, metadata 1, from disk 0
BTRFS error (device dm-2): root entry 256, num_refs 18446744073709551615
BTRFS error (device dm-2): root entry 273, num_refs 18446744073709551615
BTRFS error (device dm-2): Ref action 3, root 273, ref_root 273, parent 0, owner 0, offset 0, num_refs 1
btrfs_force_cow_block+0x129/0x7d0 [btrfs]
btrfs_cow_block+0x10a/0x250 [btrfs]
btrfs_search_slot+0x5eb/0xf40 [btrfs]
btrfs_insert_empty_items+0x3a/0x70 [btrfs]
insert_with_overflow+0x53/0x130 [btrfs]
btrfs_insert_dir_item+0x125/0x290 [btrfs]
btrfs_add_link+0xaa/0x410 [btrfs]
btrfs_rename+0x5ea/0xcd0 [btrfs]
btrfs_rename2+0x28/0x60 [btrfs]
vfs_rename+0x5b2/0xe10
filename_renameat2+0x244/0x430
__x64_sys_rename+0x48/0x70
do_syscall_64+0xe1/0x790
entry_SYSCALL_64_after_hwframe+0x4b/0x53
BTRFS error (device dm-2): Ref action 2, root 273, ref_root 273, parent 0, owner 0, offset 0, num_refs 18446744073709551615
btrfs_force_cow_block+0x327/0x7d0 [btrfs]
btrfs_cow_block+0x10a/0x250 [btrfs]
btrfs_search_slot+0x5eb/0xf40 [btrfs]
btrfs_lookup_file_extent+0x4d/0x70 [btrfs]
btrfs_drop_extents+0x151/0xf00 [btrfs]
insert_reserved_file_extent+0xfe/0x3e0 [btrfs]
btrfs_finish_one_ordered+0x549/0xc40 [btrfs]
btrfs_work_helper+0xde/0x350 [btrfs]
process_one_work+0x198/0x380
worker_thread+0x1c8/0x330
kthread+0xee/0x120
ret_from_fork+0x28f/0x310
ret_from_fork_asm+0x11/0x20
BTRFS error (device dm-2): Ref action 1, root 273, ref_root 0, parent 4594335744, owner 0, offset 0, num_refs 1
__btrfs_mod_ref+0x1c5/0x2d0 [btrfs]
btrfs_copy_root+0x262/0x390 [btrfs]
create_reloc_root+0xb9/0x370 [btrfs]
btrfs_init_reloc_root+0xb0/0x1b0 [btrfs]
record_root_in_trans+0xa6/0xd0 [btrfs]
create_pending_snapshot+0x383/0xf00 [btrfs]
create_pending_snapshots+0x9b/0xc0 [btrfs]
btrfs_commit_transaction+0x280/0xeb0 [btrfs]
prepare_to_relocate+0x147/0x200 [btrfs]
relocate_block_group+0x6b/0x5e0 [btrfs]
btrfs_relocate_block_group+0x92c/0x2380 [btrfs]
btrfs_relocate_chunk+0x3f/0x1a0 [btrfs]
btrfs_balance+0xa2c/0x19c0 [btrfs]
btrfs_ioctl+0x2839/0x2d30 [btrfs]
__x64_sys_ioctl+0x416/0x9a0
do_syscall_64+0xe1/0x790The above shows the direct cause, Ref action 3 is the oldest operation, which shows the tree block is created by COW. Then ref action 2 shows it's COWed away, by a metadata update, meaning the tree block is already released, should not be referred any more.
Then the final one, is trying to create a reloc tree for subvolume 273, and that reloc root creation is referring to the already dropped tree block.
The root cause is that, during qgroup_account_snapshot(), we are calling record_root_in_trans() with "force = true".
So if the root has no reloc root, we will create one, but at that timing it's already too late.
Normally reloc root should be created before the commit and current roots diverge, to avoid the same problem we are hitting.
But during relocation initialization, we are committing the current running transaction, with a new reloc_control attached halfway.
And if qgroup is enabled, the record_root_in_trans() with "force = true" calls will force reloc root creation even if we do not and should not create reloc root at that timing.
[FIX]
Do not force reloc root creation during record_root_in_trans() with "force = true" cases, which is only called by qgroup_account_snapshot().
If we're really under relocation, the reloc root should be created way early, before the commit and current root diverge. If the root has no reloc tree yet, it means we're still initializing the reloc, and do not need a reloc root.
So skipping the reloc tree creation in qgroup_account_snapshot() should be safe.
--- fs/btrfs/transaction.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/btrfs/transaction.c +12 -1
@@ -456,8 +456,19 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans,* through btrfs_record_root_in_trans without having to take the * lock. smp_wmb() makes sure that all the writes above are * done before we pop in the zero below +* +* If @force is true, it means the call is from +* qgroup_account_snapshot(), which only requires radix tree +* tracking. +* We should not force reloc root creation here, as the root +* may have already been modified, and in that case +* root->commit_root has already been dropped. +* +* Using that commit root will cause the reloc root to refer +* to a deleted extent, causing extent tree corruption. */ - ret =btrfs_init_reloc_root (trans, root); +if (!force) + ret =btrfs_init_reloc_root (trans, root);smp_mb__before_atomic ();clear_bit (BTRFS_ROOT_IN_TRANS_SETUP , &root->state); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 624/1102 ipvs: fix reversed sequence option serialization
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit b04578b74f2d3755548fe9e829e3b2a6c6f966a1 ]
hton_seq() expects the host-order source first and the unaligned network-order destination second. The version 1 sync sender passes these arguments in reverse for both sequence blocks. This leaves 24 bytes of the kmalloc-backed message unwritten. It may disclose stale heap data and replace the live connection sequence state with values read from the buffer.
Pass the connection sequence state as the source and the message payload as the destination for both blocks.
--- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/ipvs/ip_vs_sync.c +2 -2
@@ -748,9 +748,9 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)if (cp->flags &IP_VS_CONN_F_SEQ_MASK ) { *(p++) =IPVS_OPT_SEQ_DATA ; *(p++) =sizeof (struct ip_vs_sync_conn_options ); -hton_seq ((struct ip_vs_seq *)p, &cp->in_seq); +hton_seq (&cp->in_seq, (struct ip_vs_seq *)p); p +=sizeof (struct ip_vs_seq ); -hton_seq ((struct ip_vs_seq *)p, &cp->out_seq); +hton_seq (&cp->out_seq, (struct ip_vs_seq *)p); p +=sizeof (struct ip_vs_seq ); }/* Handle pe data */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 625/1102 netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joas Antonio dos Santos <[email protected]>
[ Upstream commit e8f8231824b5815f57ce62cba116e511b10196de ]
sip_skip_whitespace() returns dptr unchanged when its own loop exhausts the buffer (dptr == limit), instead of NULL like its sibling sip_follow_continuation() returns on its own "no more data" path.
ct_sip_get_header() only checks for NULL after calling it:
dptr = sip_skip_whitespace(dptr, limit);
if (dptr == NULL)
break;
if (*dptr != ':' || ++dptr >= limit)
break;so a recognized header name followed only by spaces/tabs running to the exact end of the SIP payload, with no colon, makes the very next statement read one byte past the buffer.
Make both "no more data" outcomes return NULL, matching the convention sip_follow_continuation() already uses and that both existing callers already check for.
--- net/netfilter/nf_conntrack_sip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -429,7 +429,7 @@ static const char *sip_skip_whitespace(const char *dptr, const char *limit) dptr =sip_follow_continuation (dptr, limit);break ; } -return dptr; +return dptr < limit ? dptr :NULL ; }/* Search within a SIP header value, dealing with continuation lines */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 626/1102 tracing/probes: Fix use-after-free on field name/type of events with multiple probes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henry Martin <[email protected]>
[ Upstream commit 86b7a239ec6b14a7544200ede85474c6f5526049 ]
The fields of a probe-based dynamic event (kprobe, uprobe, eprobe and fprobe events) are created in traceprobe_define_arg_fields() by handing the probe_arg name/type strings to trace_define_field(), which only stores the pointers without copying. Those strings are owned by the trace_probe and are freed when that probe is removed.
An event can have several probes attached. The field list is defined only once, by the first probe that registers the event, but it is kept alive by any surviving sibling probe. Deleting just that first probe by symbol -
# primary A: fields are defined from A's args echo 'p:kprobes/ev vfs_read a1=$arg1' > kprobe_events # append B: shares A's event call echo 'p:kprobes/ev vfs_write a1=$arg1' >> kprobe_events # delete only A (matched by symbol), B survives echo '-:kprobes/ev vfs_read' >> kprobe_events
frees A's args (trace_probe_cleanup() -> traceprobe_free_probe_arg()), but trace_probe_unlink() keeps the trace_probe_event because the probe list is not empty. The event call stays registered via B while its fields now reference freed memory. Any field lookup then reads it, e.g.
echo 'a1 == 1' > events/kprobes/ev/filter
BUG: KASAN: slab-use-after-free in strcmp+0xa7/0xb0 Call Trace: strcmp trace_find_event_field parse_pred process_preds create_filter apply_event_filter event_filter_write
field->name references parg->name (kstrdup'd, freed with the probe) and, for array arguments, field->type references parg->fmt (kmalloc'd, freed with the probe) - the scalar type otherwise points at the static fmttype rodata, which is safe.
Have traceprobe_define_arg_fields() duplicate the name and type strings and anchor the copies on the trace_probe_event, which embeds the event call and outlives every individual probe; trace_probe_event_free() releases them.
The reproducer above triggers reliably; the field lookup and the delete both run under event_mutex, so this is a dangling reference after removal rather than a race.
The issue was found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab.
--- kernel/trace/trace_probe.c | 48 +++++++++++++++++++++++++++++++++++++- kernel/trace/trace_probe.h | 2 ++ 2 files changed, 49 insertions(+), 1 deletion(-)
kernel/trace/trace_probe.c +47 -1
@@ -1983,19 +1983,60 @@ int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptypeint traceprobe_define_arg_fields (struct trace_event_call *event_call,size_t offset,struct trace_probe *tp) { +struct trace_probe_event *tpe =trace_probe_event_from_call (event_call);int ret, i; +/* +* A field created by trace_define_field() only stores the name and +* type pointers, it does not copy the strings. Here they point into +* the probe_arg of @tp, which is freed when @tp is removed. For an +* event with multiple probes attached, the field list is defined +* once by the first probe but kept alive by the surviving siblings, +* so removing that first probe would leave the fields referencing +* freed memory. Duplicate the strings and anchor the copies on the +* trace_probe_event, which lives as long as the field list itself. +* +* event_define_fields() ignores the return value of this hook, so +* if a previous attempt failed before creating any field, it may +* call here again. Release duplicates left behind by such an +* attempt before starting over. +*/ +for (i =0 ; i < tpe->nr_field_strings; i++) +kfree (tpe->field_strings[i]); +kfree (tpe->field_strings); + tpe->field_strings =NULL ; + tpe->nr_field_strings =0 ; + +if (tp->nr_args) { + tpe->field_strings =kcalloc (tp->nr_args *2 ,sizeof (char *), +GFP_KERNEL ); +if (!tpe->field_strings) +return -ENOMEM ; + } +/* Set argument names as fields */ for (i =0 ; i < tp->nr_args; i++) {struct probe_arg *parg = &tp->args[i];const char *fmt = parg->type->fmttype;int size = parg->type->size; +char *name, *type;if (parg->fmt) fmt = parg->fmt;if (parg->count) size *= parg->count; - ret =trace_define_field (event_call, fmt, parg->name, + + name =kstrdup (parg->name,GFP_KERNEL ); + type =kstrdup (fmt,GFP_KERNEL ); +if (!name || !type) { +kfree (name); +kfree (type); +return -ENOMEM ; + } + tpe->field_strings[tpe->nr_field_strings++] = name; + tpe->field_strings[tpe->nr_field_strings++] = type; + + ret =trace_define_field (event_call, type, name, offset + parg->offset, size, parg->type->is_signed,FILTER_OTHER );
@@ -2007,6 +2048,11 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,static void trace_probe_event_free (struct trace_probe_event *tpe) { +int i; + +for (i =0 ; i < tpe->nr_field_strings; i++) +kfree (tpe->field_strings[i]); +kfree (tpe->field_strings);kfree (tpe->class.system);kfree (tpe->call.name);kfree (tpe->call.print_fmt);
kernel/trace/trace_probe.h +2 -0
@@ -253,6 +253,8 @@ struct trace_probe_event {struct trace_event_call call;struct list_head files;struct list_head probes; +char **field_strings; +int nr_field_strings;struct trace_uprobe_filter filter[]; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 627/1102 bpf: reject BPF_PSEUDO_FUNC reference to the main program
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 374b2c5561db80fcdd7cdce44af37a49416f61c7 ]
fixups.c:jit_subprogs() rewrites BPF_PSEUDO_FUNC loads to contain real function addresses. This function is invoked from bpf_jit_subprogs() only when env->subprog_cnt > 1. Meaning that for any program like below:
int main(void *ctx) {
void *ptr = main;
...
bpf_timer_set_callback(..., ptr);
...
}The 'ptr' won't be ever converted to contain an address.
In combination with e.g. bpf_timer_set_callback() this would lead to a function call at a bogus address.
Instead of complicating the implementation, just assume that no useful program needs main to be a sync or async callback and reject BPF_PSEUDO_FUNC loads for the main subprogram.
--- kernel/bpf/verifier.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/bpf/verifier.c +9 -0
@@ -15969,6 +15969,15 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)verbose (env,"callback function not static\n" );return -EINVAL ; } +/* +* When env->subprog_cnt == 1 this instruction won't be rewritten +* to hold a real function address. Assume that no usable program +* combines e.g. main and timer callback and just reject here. +*/ +if (subprogno ==0 ) { +verbose (env,"callback function cannot be the main program\n" ); +return -EINVAL ; + } dst_reg->type =PTR_TO_FUNC ; dst_reg->subprogno = subprogno;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 628/1102 bonding: do not clear curr_active_slave prematurely when releasing all slaves
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit af602c7aa5fedc9be3043244017aef4f26c96b70 ]
When releasing all slaves during bond destruction (all == true), __bond_release_one() unconditionally clears bond->curr_active_slave to NULL in every iteration.
If a backup slave is released before the active slave, bond_alb_deinit_slave() triggers rlb_teach_disabled_mac_on_primary(), which increments the active slave dev promiscuity counter and sets bond_info->primary_is_promisc = 1.
Because bond->curr_active_slave was prematurely cleared to NULL when releasing the backup slave, the subsequent iteration releasing the active slave evaluates oldcurrent as NULL, so bond_change_active_slave(bond, NULL) is skipped. Consequently, bond_alb_handle_active_change() is never called to decrement the promiscuity counter, permanently leaking promiscuous mode on the physical device after bond teardown.
When oldcurrent == slave, bond_change_active_slave(bond, NULL) already sets bond->curr_active_slave to NULL. We only need to avoid selecting a new active slave when all == true. Replace the if (all) branch with if (!all && oldcurrent == slave).
--- drivers/net/bonding/bond_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/net/bonding/bond_main.c +1 -3
@@ -2575,9 +2575,7 @@ static int __bond_release_one(struct net_device *bond_dev,bond_alb_deinit_slave (bond, slave); } -if (all) { -RCU_INIT_POINTER (bond->curr_active_slave,NULL ); - }else if (oldcurrent == slave) { +if (!all && oldcurrent == slave) {/* Note that we hold RTNL over this sequence, so there * is no concern that another slave add/remove event * will interfere.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 629/1102 net/rds: use wq_has_sleeper() in release_in_xmit()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 6d0c8b7073913011459cf968cbbadd341e166bc3 ]
release_in_xmit() clears RDS_IN_XMIT with clear_bit_unlock() and then checks waitqueue_active() to decide whether anyone needs waking. clear_bit_unlock() is only a release operation: it orders the critical section before the bit clear, but does not order the subsequent plain load of the wait queue head after it. The waiter side does the mirror image - it adds itself to the wait queue and then tests the bit. That is the classic store-buffering pattern: the releasing CPU can read the wait queue as empty while the waiting CPU still reads the bit as set, so the sleeper is never woken.
The waiters are rds_conn_shutdown() and rds_tcp_reset_callbacks(), both in uninterruptible wait_event() with no timeout. A lost wake-up strands the shutdown worker on its single-threaded workqueue until some other sender releases the bit again - and on a connection that is being torn down precisely because it failed, there may never be another sender.
The barrier used to be there: release_in_xmit() did clear_bit() followed by smp_mb__after_atomic() until commit 1422f28826d2 ("rds:
introduce acquire/release ordering in acquire/release_in_xmit()") folded both into clear_bit_unlock(), which strengthened the lock hand-off but silently dropped the full barrier the wake-up check depends on. The refill counterpart, release_refill() in net/rds/ib_recv.c, still carries its smp_mb__after_atomic() for exactly this reason.
Use wq_has_sleeper(), which is waitqueue_active() preceded by the required full barrier.
--- net/rds/send.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
net/rds/send.c +6 -1
@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)* hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. +* +* wq_has_sleeper() supplies the full barrier that orders the wait +* queue read after the bit clear; clear_bit_unlock() alone is only +* a release and would let this check read a stale empty queue, +* losing the wake-up. */ -if (waitqueue_active (&cp->cp_waitq)) +if (wq_has_sleeper (&cp->cp_waitq))wake_up_all (&cp->cp_waitq); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 630/1102 net/rds: use clear_bit_unlock() in release_refill()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 17c4476dbb9c3bfd34193a6c22f2c3da8747134a ]
release_refill() drops the RDS_RECV_REFILL bit with a plain clear_bit(). clear_bit() has no ordering semantics, and the smp_mb__after_atomic() that follows it sits on the wrong side for a lock release: it orders the clear against the waitqueue_active() load below it, but does nothing to order the refill critical section's ring and descriptor stores before the clear itself.
That matters once connection teardown owns RDS_RECV_REFILL as a lock across the transport shutdown and path reset, rather than sampling it clear, which "net/rds: acquire the fastpath locks in rds_conn_shutdown()" later in this series arranges: on a weakly ordered architecture the teardown can win the bit and start the shutdown and reset while some of the refill's stores are not yet visible to it. The same gap existed under the sample-based scheme - a waiter that saw the bit clear had no guarantee it also observed the refill's stores - but taking the bit as a lock makes the missing release pairing load-bearing.
Switch to clear_bit_unlock(), which orders the critical section before the release, and replace the open-coded barrier-plus-waitqueue_active() with wq_has_sleeper(), whose internal full barrier keeps the store-buffering guarantee between clearing the bit and checking for sleepers. This mirrors what "net/rds: use wq_has_sleeper() in release_in_xmit()" does for RDS_IN_XMIT.
The fast-path acquire side, acquire_refill(), uses test_and_set_bit(), a full-barrier RMW that pairs with this release. The teardown at this point in the series still samples the bit, so on its own this change is release-side hardening; the shutdown-conversion patch named above makes the teardown acquire the bit with the same RMW, completing the pairing at the end of the series.
--- net/rds/ib_recv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
net/rds/ib_recv.c +2 -3
@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)static void release_refill (struct rds_connection *conn) { -clear_bit (RDS_RECV_REFILL , &conn->c_flags); -smp_mb__after_atomic (); +clear_bit_unlock (RDS_RECV_REFILL , &conn->c_flags);/* We don't use wait_on_bit()/wake_up_bit() because our waking is in a * hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. */ -if (waitqueue_active (&conn->c_waitq)) +if (wq_has_sleeper (&conn->c_waitq))wake_up_all (&conn->c_waitq); }
Signature · 1 line
2.53.0