]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 27 Jan 2024 00:20:20 +0000 (16:20 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 27 Jan 2024 00:20:20 +0000 (16:20 -0800)
added patches:
mm-rmap-fix-misplaced-parenthesis-of-a-likely.patch
mm-sparsemem-fix-race-in-accessing-memory_section-usage.patch
rename-fix-the-locking-of-subdirectories.patch
serial-sc16is7xx-change-efr-lock-to-operate-on-each-channels.patch
serial-sc16is7xx-convert-from-_raw_-to-_noinc_-regmap-functions-for-fifo.patch
serial-sc16is7xx-fix-invalid-sc16is7xx_lines-bitfield-in-case-of-probe-error.patch
serial-sc16is7xx-improve-do-while-loop-in-sc16is7xx_irq.patch
serial-sc16is7xx-improve-regmap-debugfs-by-using-one-regmap-per-port.patch
serial-sc16is7xx-remove-global-regmap-from-struct-sc16is7xx_port.patch
serial-sc16is7xx-remove-obsolete-loop-in-sc16is7xx_port_irq.patch
serial-sc16is7xx-remove-unused-line-structure-member.patch
serial-sc16is7xx-remove-wasteful-static-buffer-in-sc16is7xx_regmap_name.patch

13 files changed:
queue-6.1/mm-rmap-fix-misplaced-parenthesis-of-a-likely.patch [new file with mode: 0644]
queue-6.1/mm-sparsemem-fix-race-in-accessing-memory_section-usage.patch [new file with mode: 0644]
queue-6.1/rename-fix-the-locking-of-subdirectories.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-change-efr-lock-to-operate-on-each-channels.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-convert-from-_raw_-to-_noinc_-regmap-functions-for-fifo.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-fix-invalid-sc16is7xx_lines-bitfield-in-case-of-probe-error.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-improve-do-while-loop-in-sc16is7xx_irq.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-improve-regmap-debugfs-by-using-one-regmap-per-port.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-remove-global-regmap-from-struct-sc16is7xx_port.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-remove-obsolete-loop-in-sc16is7xx_port_irq.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-remove-unused-line-structure-member.patch [new file with mode: 0644]
queue-6.1/serial-sc16is7xx-remove-wasteful-static-buffer-in-sc16is7xx_regmap_name.patch [new file with mode: 0644]
queue-6.1/series

diff --git a/queue-6.1/mm-rmap-fix-misplaced-parenthesis-of-a-likely.patch b/queue-6.1/mm-rmap-fix-misplaced-parenthesis-of-a-likely.patch
new file mode 100644 (file)
index 0000000..83cf247
--- /dev/null
@@ -0,0 +1,64 @@
+From f67f8d4a8c1e1ebc85a6cbdb9a7266f14863461c Mon Sep 17 00:00:00 2001
+From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
+Date: Fri, 1 Dec 2023 14:59:36 -0500
+Subject: mm/rmap: fix misplaced parenthesis of a likely()
+
+From: Steven Rostedt (Google) <rostedt@goodmis.org>
+
+commit f67f8d4a8c1e1ebc85a6cbdb9a7266f14863461c upstream.
+
+Running my yearly branch profiler to see where likely/unlikely annotation
+may be added or removed, I discovered this:
+
+correct incorrect  %        Function                  File              Line
+ ------- ---------  -        --------                  ----              ----
+       0   457918 100 page_try_dup_anon_rmap         rmap.h               264
+[..]
+  458021        0   0 page_try_dup_anon_rmap         rmap.h               265
+
+I thought it was interesting that line 264 of rmap.h had a 100% incorrect
+annotation, but the line directly below it was 100% correct. Looking at the
+code:
+
+       if (likely(!is_device_private_page(page) &&
+           unlikely(page_needs_cow_for_dma(vma, page))))
+
+It didn't make sense. The "likely()" was around the entire if statement
+(not just the "!is_device_private_page(page)"), which also included the
+"unlikely()" portion of that if condition.
+
+If the unlikely portion is unlikely to be true, that would make the entire
+if condition unlikely to be true, so it made no sense at all to say the
+entire if condition is true.
+
+What is more likely to be likely is just the first part of the if statement
+before the && operation. It's likely to be a misplaced parenthesis. And
+after making the if condition broken into a likely() && unlikely(), both
+now appear to be correct!
+
+Link: https://lkml.kernel.org/r/20231201145936.5ddfdb50@gandalf.local.home
+Fixes:fb3d824d1a46c ("mm/rmap: split page_dup_rmap() into page_dup_file_rmap() and page_try_dup_anon_rmap()")
+Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Acked-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/rmap.h |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/include/linux/rmap.h
++++ b/include/linux/rmap.h
+@@ -250,8 +250,8 @@ static inline int page_try_dup_anon_rmap
+        * guarantee the pinned page won't be randomly replaced in the
+        * future on write faults.
+        */
+-      if (likely(!is_device_private_page(page) &&
+-          unlikely(page_needs_cow_for_dma(vma, page))))
++      if (likely(!is_device_private_page(page)) &&
++          unlikely(page_needs_cow_for_dma(vma, page)))
+               return -EBUSY;
+       ClearPageAnonExclusive(page);
diff --git a/queue-6.1/mm-sparsemem-fix-race-in-accessing-memory_section-usage.patch b/queue-6.1/mm-sparsemem-fix-race-in-accessing-memory_section-usage.patch
new file mode 100644 (file)
index 0000000..fe86076
--- /dev/null
@@ -0,0 +1,206 @@
+From 5ec8e8ea8b7783fab150cf86404fc38cb4db8800 Mon Sep 17 00:00:00 2001
+From: Charan Teja Kalla <quic_charante@quicinc.com>
+Date: Fri, 13 Oct 2023 18:34:27 +0530
+Subject: mm/sparsemem: fix race in accessing memory_section->usage
+
+From: Charan Teja Kalla <quic_charante@quicinc.com>
+
+commit 5ec8e8ea8b7783fab150cf86404fc38cb4db8800 upstream.
+
+The below race is observed on a PFN which falls into the device memory
+region with the system memory configuration where PFN's are such that
+[ZONE_NORMAL ZONE_DEVICE ZONE_NORMAL].  Since normal zone start and end
+pfn contains the device memory PFN's as well, the compaction triggered
+will try on the device memory PFN's too though they end up in NOP(because
+pfn_to_online_page() returns NULL for ZONE_DEVICE memory sections).  When
+from other core, the section mappings are being removed for the
+ZONE_DEVICE region, that the PFN in question belongs to, on which
+compaction is currently being operated is resulting into the kernel crash
+with CONFIG_SPASEMEM_VMEMAP enabled.  The crash logs can be seen at [1].
+
+compact_zone()                 memunmap_pages
+-------------                  ---------------
+__pageblock_pfn_to_page
+   ......
+ (a)pfn_valid():
+     valid_section()//return true
+                             (b)__remove_pages()->
+                                 sparse_remove_section()->
+                                   section_deactivate():
+                                   [Free the array ms->usage and set
+                                    ms->usage = NULL]
+     pfn_section_valid()
+     [Access ms->usage which
+     is NULL]
+
+NOTE: From the above it can be said that the race is reduced to between
+the pfn_valid()/pfn_section_valid() and the section deactivate with
+SPASEMEM_VMEMAP enabled.
+
+The commit b943f045a9af("mm/sparse: fix kernel crash with
+pfn_section_valid check") tried to address the same problem by clearing
+the SECTION_HAS_MEM_MAP with the expectation of valid_section() returns
+false thus ms->usage is not accessed.
+
+Fix this issue by the below steps:
+
+a) Clear SECTION_HAS_MEM_MAP before freeing the ->usage.
+
+b) RCU protected read side critical section will either return NULL
+   when SECTION_HAS_MEM_MAP is cleared or can successfully access ->usage.
+
+c) Free the ->usage with kfree_rcu() and set ms->usage = NULL.  No
+   attempt will be made to access ->usage after this as the
+   SECTION_HAS_MEM_MAP is cleared thus valid_section() return false.
+
+Thanks to David/Pavan for their inputs on this patch.
+
+[1] https://lore.kernel.org/linux-mm/994410bb-89aa-d987-1f50-f514903c55aa@quicinc.com/
+
+On Snapdragon SoC, with the mentioned memory configuration of PFN's as
+[ZONE_NORMAL ZONE_DEVICE ZONE_NORMAL], we are able to see bunch of
+issues daily while testing on a device farm.
+
+For this particular issue below is the log.  Though the below log is
+not directly pointing to the pfn_section_valid(){ ms->usage;}, when we
+loaded this dump on T32 lauterbach tool, it is pointing.
+
+[  540.578056] Unable to handle kernel NULL pointer dereference at
+virtual address 0000000000000000
+[  540.578068] Mem abort info:
+[  540.578070]   ESR = 0x0000000096000005
+[  540.578073]   EC = 0x25: DABT (current EL), IL = 32 bits
+[  540.578077]   SET = 0, FnV = 0
+[  540.578080]   EA = 0, S1PTW = 0
+[  540.578082]   FSC = 0x05: level 1 translation fault
+[  540.578085] Data abort info:
+[  540.578086]   ISV = 0, ISS = 0x00000005
+[  540.578088]   CM = 0, WnR = 0
+[  540.579431] pstate: 82400005 (Nzcv daif +PAN -UAO +TCO -DIT -SSBSBTYPE=--)
+[  540.579436] pc : __pageblock_pfn_to_page+0x6c/0x14c
+[  540.579454] lr : compact_zone+0x994/0x1058
+[  540.579460] sp : ffffffc03579b510
+[  540.579463] x29: ffffffc03579b510 x28: 0000000000235800 x27:000000000000000c
+[  540.579470] x26: 0000000000235c00 x25: 0000000000000068 x24:ffffffc03579b640
+[  540.579477] x23: 0000000000000001 x22: ffffffc03579b660 x21:0000000000000000
+[  540.579483] x20: 0000000000235bff x19: ffffffdebf7e3940 x18:ffffffdebf66d140
+[  540.579489] x17: 00000000739ba063 x16: 00000000739ba063 x15:00000000009f4bff
+[  540.579495] x14: 0000008000000000 x13: 0000000000000000 x12:0000000000000001
+[  540.579501] x11: 0000000000000000 x10: 0000000000000000 x9 :ffffff897d2cd440
+[  540.579507] x8 : 0000000000000000 x7 : 0000000000000000 x6 :ffffffc03579b5b4
+[  540.579512] x5 : 0000000000027f25 x4 : ffffffc03579b5b8 x3 :0000000000000001
+[  540.579518] x2 : ffffffdebf7e3940 x1 : 0000000000235c00 x0 :0000000000235800
+[  540.579524] Call trace:
+[  540.579527]  __pageblock_pfn_to_page+0x6c/0x14c
+[  540.579533]  compact_zone+0x994/0x1058
+[  540.579536]  try_to_compact_pages+0x128/0x378
+[  540.579540]  __alloc_pages_direct_compact+0x80/0x2b0
+[  540.579544]  __alloc_pages_slowpath+0x5c0/0xe10
+[  540.579547]  __alloc_pages+0x250/0x2d0
+[  540.579550]  __iommu_dma_alloc_noncontiguous+0x13c/0x3fc
+[  540.579561]  iommu_dma_alloc+0xa0/0x320
+[  540.579565]  dma_alloc_attrs+0xd4/0x108
+
+[quic_charante@quicinc.com: use kfree_rcu() in place of synchronize_rcu(), per David]
+  Link: https://lkml.kernel.org/r/1698403778-20938-1-git-send-email-quic_charante@quicinc.com
+Link: https://lkml.kernel.org/r/1697202267-23600-1-git-send-email-quic_charante@quicinc.com
+Fixes: f46edbd1b151 ("mm/sparsemem: add helpers track active portions of a section at boot")
+Signed-off-by: Charan Teja Kalla <quic_charante@quicinc.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Dan Williams <dan.j.williams@intel.com>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Mel Gorman <mgorman@techsingularity.net>
+Cc: Oscar Salvador <osalvador@suse.de>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/mmzone.h |   14 +++++++++++---
+ mm/sparse.c            |   17 +++++++++--------
+ 2 files changed, 20 insertions(+), 11 deletions(-)
+
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -1622,6 +1622,7 @@ static inline unsigned long section_nr_t
+ #define SUBSECTION_ALIGN_DOWN(pfn) ((pfn) & PAGE_SUBSECTION_MASK)
+ struct mem_section_usage {
++      struct rcu_head rcu;
+ #ifdef CONFIG_SPARSEMEM_VMEMMAP
+       DECLARE_BITMAP(subsection_map, SUBSECTIONS_PER_SECTION);
+ #endif
+@@ -1815,7 +1816,7 @@ static inline int pfn_section_valid(stru
+ {
+       int idx = subsection_map_index(pfn);
+-      return test_bit(idx, ms->usage->subsection_map);
++      return test_bit(idx, READ_ONCE(ms->usage)->subsection_map);
+ }
+ #else
+ static inline int pfn_section_valid(struct mem_section *ms, unsigned long pfn)
+@@ -1839,6 +1840,7 @@ static inline int pfn_section_valid(stru
+ static inline int pfn_valid(unsigned long pfn)
+ {
+       struct mem_section *ms;
++      int ret;
+       /*
+        * Ensure the upper PAGE_SHIFT bits are clear in the
+@@ -1852,13 +1854,19 @@ static inline int pfn_valid(unsigned lon
+       if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
+               return 0;
+       ms = __pfn_to_section(pfn);
+-      if (!valid_section(ms))
++      rcu_read_lock();
++      if (!valid_section(ms)) {
++              rcu_read_unlock();
+               return 0;
++      }
+       /*
+        * Traditionally early sections always returned pfn_valid() for
+        * the entire section-sized span.
+        */
+-      return early_section(ms) || pfn_section_valid(ms, pfn);
++      ret = early_section(ms) || pfn_section_valid(ms, pfn);
++      rcu_read_unlock();
++
++      return ret;
+ }
+ #endif
+--- a/mm/sparse.c
++++ b/mm/sparse.c
+@@ -793,6 +793,13 @@ static void section_deactivate(unsigned
+               unsigned long section_nr = pfn_to_section_nr(pfn);
+               /*
++               * Mark the section invalid so that valid_section()
++               * return false. This prevents code from dereferencing
++               * ms->usage array.
++               */
++              ms->section_mem_map &= ~SECTION_HAS_MEM_MAP;
++
++              /*
+                * When removing an early section, the usage map is kept (as the
+                * usage maps of other sections fall into the same page). It
+                * will be re-used when re-adding the section - which is then no
+@@ -800,16 +807,10 @@ static void section_deactivate(unsigned
+                * was allocated during boot.
+                */
+               if (!PageReserved(virt_to_page(ms->usage))) {
+-                      kfree(ms->usage);
+-                      ms->usage = NULL;
++                      kfree_rcu(ms->usage, rcu);
++                      WRITE_ONCE(ms->usage, NULL);
+               }
+               memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr);
+-              /*
+-               * Mark the section invalid so that valid_section()
+-               * return false. This prevents code from dereferencing
+-               * ms->usage array.
+-               */
+-              ms->section_mem_map &= ~SECTION_HAS_MEM_MAP;
+       }
+       /*
diff --git a/queue-6.1/rename-fix-the-locking-of-subdirectories.patch b/queue-6.1/rename-fix-the-locking-of-subdirectories.patch
new file mode 100644 (file)
index 0000000..3e62ad0
--- /dev/null
@@ -0,0 +1,276 @@
+From 22e111ed6c83dcde3037fc81176012721bc34c0b Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sun, 19 Nov 2023 20:25:58 -0500
+Subject: rename(): fix the locking of subdirectories
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit 22e111ed6c83dcde3037fc81176012721bc34c0b upstream.
+
+       We should never lock two subdirectories without having taken
+->s_vfs_rename_mutex; inode pointer order or not, the "order" proposed
+in 28eceeda130f "fs: Lock moved directories" is not transitive, with
+the usual consequences.
+
+       The rationale for locking renamed subdirectory in all cases was
+the possibility of race between rename modifying .. in a subdirectory to
+reflect the new parent and another thread modifying the same subdirectory.
+For a lot of filesystems that's not a problem, but for some it can lead
+to trouble (e.g. the case when short directory contents is kept in the
+inode, but creating a file in it might push it across the size limit
+and copy its contents into separate data block(s)).
+
+       However, we need that only in case when the parent does change -
+otherwise ->rename() doesn't need to do anything with .. entry in the
+first place.  Some instances are lazy and do a tautological update anyway,
+but it's really not hard to avoid.
+
+Amended locking rules for rename():
+       find the parent(s) of source and target
+       if source and target have the same parent
+               lock the common parent
+       else
+               lock ->s_vfs_rename_mutex
+               lock both parents, in ancestor-first order; if neither
+               is an ancestor of another, lock the parent of source
+               first.
+       find the source and target.
+       if source and target have the same parent
+               if operation is an overwriting rename of a subdirectory
+                       lock the target subdirectory
+       else
+               if source is a subdirectory
+                       lock the source
+               if target is a subdirectory
+                       lock the target
+       lock non-directories involved, in inode pointer order if both
+       source and target are such.
+
+That way we are guaranteed that parents are locked (for obvious reasons),
+that any renamed non-directory is locked (nfsd relies upon that),
+that any victim is locked (emptiness check needs that, among other things)
+and subdirectory that changes parent is locked (needed to protect the update
+of .. entries).  We are also guaranteed that any operation locking more
+than one directory either takes ->s_vfs_rename_mutex or locks a parent
+followed by its child.
+
+Cc: stable@vger.kernel.org
+Fixes: 28eceeda130f "fs: Lock moved directories"
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/filesystems/directory-locking.rst |   29 ++++++-----
+ Documentation/filesystems/locking.rst           |    5 +-
+ Documentation/filesystems/porting.rst           |   18 +++++++
+ fs/namei.c                                      |   60 ++++++++++++++----------
+ 4 files changed, 74 insertions(+), 38 deletions(-)
+
+--- a/Documentation/filesystems/directory-locking.rst
++++ b/Documentation/filesystems/directory-locking.rst
+@@ -22,13 +22,16 @@ exclusive.
+ 3) object removal.  Locking rules: caller locks parent, finds victim,
+ locks victim and calls the method.  Locks are exclusive.
+-4) rename() that is _not_ cross-directory.  Locking rules: caller locks the
+-parent and finds source and target.  We lock both (provided they exist).  If we
+-need to lock two inodes of different type (dir vs non-dir), we lock directory
+-first.  If we need to lock two inodes of the same type, lock them in inode
+-pointer order.  Then call the method.  All locks are exclusive.
+-NB: we might get away with locking the source (and target in exchange
+-case) shared.
++4) rename() that is _not_ cross-directory.  Locking rules: caller locks
++the parent and finds source and target.  Then we decide which of the
++source and target need to be locked.  Source needs to be locked if it's a
++non-directory; target - if it's a non-directory or about to be removed.
++Take the locks that need to be taken, in inode pointer order if need
++to take both (that can happen only when both source and target are
++non-directories - the source because it wouldn't be locked otherwise
++and the target because mixing directory and non-directory is allowed
++only with RENAME_EXCHANGE, and that won't be removing the target).
++After the locks had been taken, call the method.  All locks are exclusive.
+ 5) link creation.  Locking rules:
+@@ -44,20 +47,17 @@ rules:
+       * lock the filesystem
+       * lock parents in "ancestors first" order. If one is not ancestor of
+-        the other, lock them in inode pointer order.
++        the other, lock the parent of source first.
+       * find source and target.
+       * if old parent is equal to or is a descendent of target
+         fail with -ENOTEMPTY
+       * if new parent is equal to or is a descendent of source
+         fail with -ELOOP
+-      * Lock both the source and the target provided they exist. If we
+-        need to lock two inodes of different type (dir vs non-dir), we lock
+-        the directory first. If we need to lock two inodes of the same type,
+-        lock them in inode pointer order.
++      * Lock subdirectories involved (source before target).
++      * Lock non-directories involved, in inode pointer order.
+       * call the method.
+-All ->i_rwsem are taken exclusive.  Again, we might get away with locking
+-the source (and target in exchange case) shared.
++All ->i_rwsem are taken exclusive.
+ The rules above obviously guarantee that all directories that are going to be
+ read, modified or removed by method will be locked by caller.
+@@ -67,6 +67,7 @@ If no directory is its own ancestor, the
+ Proof:
++[XXX: will be updated once we are done massaging the lock_rename()]
+       First of all, at any moment we have a linear ordering of the
+       objects - A < B iff (A is an ancestor of B) or (B is not an ancestor
+         of A and ptr(A) < ptr(B)).
+--- a/Documentation/filesystems/locking.rst
++++ b/Documentation/filesystems/locking.rst
+@@ -99,7 +99,7 @@ symlink:     exclusive
+ mkdir:                exclusive
+ unlink:               exclusive (both)
+ rmdir:                exclusive (both)(see below)
+-rename:               exclusive (all) (see below)
++rename:               exclusive (both parents, some children) (see below)
+ readlink:     no
+ get_link:     no
+ setattr:      exclusive
+@@ -119,6 +119,9 @@ fileattr_set:      exclusive
+       Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_rwsem
+       exclusive on victim.
+       cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
++      ->unlink() and ->rename() have ->i_rwsem exclusive on all non-directories
++      involved.
++      ->rename() has ->i_rwsem exclusive on any subdirectory that changes parent.
+ See Documentation/filesystems/directory-locking.rst for more detailed discussion
+ of the locking scheme for directory operations.
+--- a/Documentation/filesystems/porting.rst
++++ b/Documentation/filesystems/porting.rst
+@@ -943,3 +943,21 @@ file pointer instead of struct dentry po
+ changed to simplify callers.  The passed file is in a non-open state and on
+ success must be opened before returning (e.g. by calling
+ finish_open_simple()).
++
++---
++
++**mandatory**
++
++If ->rename() update of .. on cross-directory move needs an exclusion with
++directory modifications, do *not* lock the subdirectory in question in your
++->rename() - it's done by the caller now [that item should've been added in
++28eceeda130f "fs: Lock moved directories"].
++
++---
++
++**mandatory**
++
++On same-directory ->rename() the (tautological) update of .. is not protected
++by any locks; just don't do it if the old parent is the same as the new one.
++We really can't lock two subdirectories in same-directory rename - not without
++deadlocks.
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -3017,20 +3017,14 @@ static struct dentry *lock_two_directori
+       p = d_ancestor(p2, p1);
+       if (p) {
+               inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
+-              inode_lock_nested(p1->d_inode, I_MUTEX_CHILD);
++              inode_lock_nested(p1->d_inode, I_MUTEX_PARENT2);
+               return p;
+       }
+       p = d_ancestor(p1, p2);
+-      if (p) {
+-              inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
+-              inode_lock_nested(p2->d_inode, I_MUTEX_CHILD);
+-              return p;
+-      }
+-
+-      lock_two_inodes(p1->d_inode, p2->d_inode,
+-                      I_MUTEX_PARENT, I_MUTEX_PARENT2);
+-      return NULL;
++      inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
++      inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2);
++      return p;
+ }
+ /*
+@@ -4735,11 +4729,12 @@ SYSCALL_DEFINE2(link, const char __user
+  *
+  *    a) we can get into loop creation.
+  *    b) race potential - two innocent renames can create a loop together.
+- *       That's where 4.4 screws up. Current fix: serialization on
++ *       That's where 4.4BSD screws up. Current fix: serialization on
+  *       sb->s_vfs_rename_mutex. We might be more accurate, but that's another
+  *       story.
+- *    c) we have to lock _four_ objects - parents and victim (if it exists),
+- *       and source.
++ *    c) we may have to lock up to _four_ objects - parents and victim (if it exists),
++ *       and source (if it's a non-directory or a subdirectory that moves to
++ *       different parent).
+  *       And that - after we got ->i_mutex on parents (until then we don't know
+  *       whether the target exists).  Solution: try to be smart with locking
+  *       order for inodes.  We rely on the fact that tree topology may change
+@@ -4771,6 +4766,7 @@ int vfs_rename(struct renamedata *rd)
+       bool new_is_dir = false;
+       unsigned max_links = new_dir->i_sb->s_max_links;
+       struct name_snapshot old_name;
++      bool lock_old_subdir, lock_new_subdir;
+       if (source == target)
+               return 0;
+@@ -4824,15 +4820,32 @@ int vfs_rename(struct renamedata *rd)
+       take_dentry_name_snapshot(&old_name, old_dentry);
+       dget(new_dentry);
+       /*
+-       * Lock all moved children. Moved directories may need to change parent
+-       * pointer so they need the lock to prevent against concurrent
+-       * directory changes moving parent pointer. For regular files we've
+-       * historically always done this. The lockdep locking subclasses are
+-       * somewhat arbitrary but RENAME_EXCHANGE in particular can swap
+-       * regular files and directories so it's difficult to tell which
+-       * subclasses to use.
++       * Lock children.
++       * The source subdirectory needs to be locked on cross-directory
++       * rename or cross-directory exchange since its parent changes.
++       * The target subdirectory needs to be locked on cross-directory
++       * exchange due to parent change and on any rename due to becoming
++       * a victim.
++       * Non-directories need locking in all cases (for NFS reasons);
++       * they get locked after any subdirectories (in inode address order).
++       *
++       * NOTE: WE ONLY LOCK UNRELATED DIRECTORIES IN CROSS-DIRECTORY CASE.
++       * NEVER, EVER DO THAT WITHOUT ->s_vfs_rename_mutex.
+        */
+-      lock_two_inodes(source, target, I_MUTEX_NORMAL, I_MUTEX_NONDIR2);
++      lock_old_subdir = new_dir != old_dir;
++      lock_new_subdir = new_dir != old_dir || !(flags & RENAME_EXCHANGE);
++      if (is_dir) {
++              if (lock_old_subdir)
++                      inode_lock_nested(source, I_MUTEX_CHILD);
++              if (target && (!new_is_dir || lock_new_subdir))
++                      inode_lock(target);
++      } else if (new_is_dir) {
++              if (lock_new_subdir)
++                      inode_lock_nested(target, I_MUTEX_CHILD);
++              inode_lock(source);
++      } else {
++              lock_two_nondirectories(source, target);
++      }
+       error = -EPERM;
+       if (IS_SWAPFILE(source) || (target && IS_SWAPFILE(target)))
+@@ -4880,8 +4893,9 @@ int vfs_rename(struct renamedata *rd)
+                       d_exchange(old_dentry, new_dentry);
+       }
+ out:
+-      inode_unlock(source);
+-      if (target)
++      if (!is_dir || lock_old_subdir)
++              inode_unlock(source);
++      if (target && (!new_is_dir || lock_new_subdir))
+               inode_unlock(target);
+       dput(new_dentry);
+       if (!error) {
diff --git a/queue-6.1/serial-sc16is7xx-change-efr-lock-to-operate-on-each-channels.patch b/queue-6.1/serial-sc16is7xx-change-efr-lock-to-operate-on-each-channels.patch
new file mode 100644 (file)
index 0000000..4f64a6e
--- /dev/null
@@ -0,0 +1,206 @@
+From 4409df5866b7ff7686ba27e449ca97a92ee063c9 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 11 Dec 2023 12:13:51 -0500
+Subject: serial: sc16is7xx: change EFR lock to operate on each channels
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit 4409df5866b7ff7686ba27e449ca97a92ee063c9 upstream.
+
+Now that the driver has been converted to use one regmap per port, change
+efr locking to operate on a channel basis instead of on the whole IC.
+
+Fixes: 3837a0379533 ("serial: sc16is7xx: improve regmap debugfs by using one regmap per port")
+Cc:  <stable@vger.kernel.org> # 6.1.x: 3837a03 serial: sc16is7xx: improve regmap debugfs by using one regmap per port
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231211171353.2901416-5-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   49 +++++++++++++++++++++--------------------
+ 1 file changed, 26 insertions(+), 23 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -324,6 +324,7 @@ struct sc16is7xx_one_config {
+ struct sc16is7xx_one {
+       struct uart_port                port;
+       struct regmap                   *regmap;
++      struct mutex                    efr_lock; /* EFR registers access */
+       struct kthread_work             tx_work;
+       struct kthread_work             reg_work;
+       struct kthread_delayed_work     ms_work;
+@@ -343,7 +344,6 @@ struct sc16is7xx_port {
+       unsigned char                   buf[SC16IS7XX_FIFO_SIZE];
+       struct kthread_worker           kworker;
+       struct task_struct              *kworker_task;
+-      struct mutex                    efr_lock;
+       struct sc16is7xx_one            p[];
+ };
+@@ -496,7 +496,6 @@ static bool sc16is7xx_regmap_precious(st
+ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
+ {
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       u8 lcr;
+       u8 prescaler = 0;
+@@ -520,7 +519,7 @@ static int sc16is7xx_set_baud(struct uar
+        * because the bulk of the interrupt processing is run as a workqueue
+        * job in thread context.
+        */
+-      mutex_lock(&s->efr_lock);
++      mutex_lock(&one->efr_lock);
+       lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
+@@ -539,7 +538,7 @@ static int sc16is7xx_set_baud(struct uar
+       /* Put LCR back to the normal mode */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+-      mutex_unlock(&s->efr_lock);
++      mutex_unlock(&one->efr_lock);
+       sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
+                             SC16IS7XX_MCR_CLKSEL_BIT,
+@@ -709,11 +708,10 @@ static unsigned int sc16is7xx_get_hwmctr
+ static void sc16is7xx_update_mlines(struct sc16is7xx_one *one)
+ {
+       struct uart_port *port = &one->port;
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+       unsigned long flags;
+       unsigned int status, changed;
+-      lockdep_assert_held_once(&s->efr_lock);
++      lockdep_assert_held_once(&one->efr_lock);
+       status = sc16is7xx_get_hwmctrl(port);
+       changed = status ^ one->old_mctrl;
+@@ -739,15 +737,20 @@ static void sc16is7xx_update_mlines(stru
+ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
+ {
++      bool rc = true;
+       struct uart_port *port = &s->p[portno].port;
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
++
++      mutex_lock(&one->efr_lock);
+       do {
+               unsigned int iir, rxlen;
+-              struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+               iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
+-              if (iir & SC16IS7XX_IIR_NO_INT_BIT)
+-                      return false;
++              if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
++                      rc = false;
++                      goto out_port_irq;
++              }
+               iir &= SC16IS7XX_IIR_ID_MASK;
+@@ -787,15 +790,17 @@ static bool sc16is7xx_port_irq(struct sc
+                       break;
+               }
+       } while (0);
+-      return true;
++
++out_port_irq:
++      mutex_unlock(&one->efr_lock);
++
++      return rc;
+ }
+ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
+ {
+       struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
+-      mutex_lock(&s->efr_lock);
+-
+       while (1) {
+               bool keep_polling = false;
+               int i;
+@@ -806,24 +811,22 @@ static irqreturn_t sc16is7xx_irq(int irq
+                       break;
+       }
+-      mutex_unlock(&s->efr_lock);
+-
+       return IRQ_HANDLED;
+ }
+ static void sc16is7xx_tx_proc(struct kthread_work *ws)
+ {
+       struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       unsigned long flags;
+       if ((port->rs485.flags & SER_RS485_ENABLED) &&
+           (port->rs485.delay_rts_before_send > 0))
+               msleep(port->rs485.delay_rts_before_send);
+-      mutex_lock(&s->efr_lock);
++      mutex_lock(&one->efr_lock);
+       sc16is7xx_handle_tx(port);
+-      mutex_unlock(&s->efr_lock);
++      mutex_unlock(&one->efr_lock);
+       spin_lock_irqsave(&port->lock, flags);
+       sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
+@@ -930,9 +933,9 @@ static void sc16is7xx_ms_proc(struct kth
+       struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
+       if (one->port.state) {
+-              mutex_lock(&s->efr_lock);
++              mutex_lock(&one->efr_lock);
+               sc16is7xx_update_mlines(one);
+-              mutex_unlock(&s->efr_lock);
++              mutex_unlock(&one->efr_lock);
+               kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ);
+       }
+@@ -1016,7 +1019,6 @@ static void sc16is7xx_set_termios(struct
+                                 struct ktermios *termios,
+                                 const struct ktermios *old)
+ {
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       unsigned int lcr, flow = 0;
+       int baud;
+@@ -1075,7 +1077,7 @@ static void sc16is7xx_set_termios(struct
+               port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
+       /* As above, claim the mutex while accessing the EFR. */
+-      mutex_lock(&s->efr_lock);
++      mutex_lock(&one->efr_lock);
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+                            SC16IS7XX_LCR_CONF_MODE_B);
+@@ -1105,7 +1107,7 @@ static void sc16is7xx_set_termios(struct
+       /* Update LCR register */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+-      mutex_unlock(&s->efr_lock);
++      mutex_unlock(&one->efr_lock);
+       /* Get baud rate generator configuration */
+       baud = uart_get_baud_rate(port, termios, old,
+@@ -1516,7 +1518,6 @@ static int sc16is7xx_probe(struct device
+       s->devtype = devtype;
+       dev_set_drvdata(dev, s);
+-      mutex_init(&s->efr_lock);
+       kthread_init_worker(&s->kworker);
+       s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
+@@ -1559,6 +1560,8 @@ static int sc16is7xx_probe(struct device
+                       goto out_ports;
+               }
++              mutex_init(&s->p[i].efr_lock);
++
+               ret = uart_get_rs485_mode(&s->p[i].port);
+               if (ret)
+                       goto out_ports;
diff --git a/queue-6.1/serial-sc16is7xx-convert-from-_raw_-to-_noinc_-regmap-functions-for-fifo.patch b/queue-6.1/serial-sc16is7xx-convert-from-_raw_-to-_noinc_-regmap-functions-for-fifo.patch
new file mode 100644 (file)
index 0000000..770850f
--- /dev/null
@@ -0,0 +1,83 @@
+From dbf4ab821804df071c8b566d9813083125e6d97b Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 11 Dec 2023 12:13:52 -0500
+Subject: serial: sc16is7xx: convert from _raw_ to _noinc_ regmap functions for FIFO
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit dbf4ab821804df071c8b566d9813083125e6d97b upstream.
+
+The SC16IS7XX IC supports a burst mode to access the FIFOs where the
+initial register address is sent ($00), followed by all the FIFO data
+without having to resend the register address each time. In this mode, the
+IC doesn't increment the register address for each R/W byte.
+
+The regmap_raw_read() and regmap_raw_write() are functions which can
+perform IO over multiple registers. They are currently used to read/write
+from/to the FIFO, and although they operate correctly in this burst mode on
+the SPI bus, they would corrupt the regmap cache if it was not disabled
+manually. The reason is that when the R/W size is more than 1 byte, these
+functions assume that the register address is incremented and handle the
+cache accordingly.
+
+Convert FIFO R/W functions to use the regmap _noinc_ versions in order to
+remove the manual cache control which was a workaround when using the
+_raw_ versions. FIFO registers are properly declared as volatile so
+cache will not be used/updated for FIFO accesses.
+
+Fixes: dfeae619d781 ("serial: sc16is7xx")
+Cc:  <stable@vger.kernel.org>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231211171353.2901416-6-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   17 +++++++++++------
+ 1 file changed, 11 insertions(+), 6 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -383,9 +383,7 @@ static void sc16is7xx_fifo_read(struct u
+       struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-      regcache_cache_bypass(one->regmap, true);
+-      regmap_raw_read(one->regmap, SC16IS7XX_RHR_REG, s->buf, rxlen);
+-      regcache_cache_bypass(one->regmap, false);
++      regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, s->buf, rxlen);
+ }
+ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send)
+@@ -400,9 +398,7 @@ static void sc16is7xx_fifo_write(struct
+       if (unlikely(!to_send))
+               return;
+-      regcache_cache_bypass(one->regmap, true);
+-      regmap_raw_write(one->regmap, SC16IS7XX_THR_REG, s->buf, to_send);
+-      regcache_cache_bypass(one->regmap, false);
++      regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, s->buf, to_send);
+ }
+ static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
+@@ -494,6 +490,11 @@ static bool sc16is7xx_regmap_precious(st
+       return false;
+ }
++static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg)
++{
++      return reg == SC16IS7XX_RHR_REG;
++}
++
+ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
+ {
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+@@ -1699,6 +1700,10 @@ static struct regmap_config regcfg = {
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = sc16is7xx_regmap_volatile,
+       .precious_reg = sc16is7xx_regmap_precious,
++      .writeable_noinc_reg = sc16is7xx_regmap_noinc,
++      .readable_noinc_reg = sc16is7xx_regmap_noinc,
++      .max_raw_read = SC16IS7XX_FIFO_SIZE,
++      .max_raw_write = SC16IS7XX_FIFO_SIZE,
+       .max_register = SC16IS7XX_EFCR_REG,
+ };
diff --git a/queue-6.1/serial-sc16is7xx-fix-invalid-sc16is7xx_lines-bitfield-in-case-of-probe-error.patch b/queue-6.1/serial-sc16is7xx-fix-invalid-sc16is7xx_lines-bitfield-in-case-of-probe-error.patch
new file mode 100644 (file)
index 0000000..b5400c2
--- /dev/null
@@ -0,0 +1,128 @@
+From 8a1060ce974919f2a79807527ad82ac39336eda2 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Thu, 21 Dec 2023 18:18:08 -0500
+Subject: serial: sc16is7xx: fix invalid sc16is7xx_lines bitfield in case of probe error
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit 8a1060ce974919f2a79807527ad82ac39336eda2 upstream.
+
+If an error occurs during probing, the sc16is7xx_lines bitfield may be left
+in a state that doesn't represent the correct state of lines allocation.
+
+For example, in a system with two SC16 devices, if an error occurs only
+during probing of channel (port) B of the second device, sc16is7xx_lines
+final state will be 00001011b instead of the expected 00000011b.
+
+This is caused in part because of the "i--" in the for/loop located in
+the out_ports: error path.
+
+Fix this by checking the return value of uart_add_one_port() and set line
+allocation bit only if this was successful. This allows the refactor of
+the obfuscated for(i--...) loop in the error path, and properly call
+uart_remove_one_port() only when needed, and properly unset line allocation
+bits.
+
+Also use same mechanism in remove() when calling uart_remove_one_port().
+
+Fixes: c64349722d14 ("sc16is7xx: support multiple devices")
+Cc:  <stable@vger.kernel.org>
+Cc: Yury Norov <yury.norov@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231221231823.2327894-2-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   44 ++++++++++++++++-------------------------
+ 1 file changed, 18 insertions(+), 26 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -409,19 +409,6 @@ static void sc16is7xx_port_update(struct
+       regmap_update_bits(one->regmap, reg, mask, val);
+ }
+-static int sc16is7xx_alloc_line(void)
+-{
+-      int i;
+-
+-      BUILD_BUG_ON(SC16IS7XX_MAX_DEVS > BITS_PER_LONG);
+-
+-      for (i = 0; i < SC16IS7XX_MAX_DEVS; i++)
+-              if (!test_and_set_bit(i, &sc16is7xx_lines))
+-                      break;
+-
+-      return i;
+-}
+-
+ static void sc16is7xx_power(struct uart_port *port, int on)
+ {
+       sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
+@@ -1534,6 +1521,13 @@ static int sc16is7xx_probe(struct device
+                    SC16IS7XX_IOCONTROL_SRESET_BIT);
+       for (i = 0; i < devtype->nr_uart; ++i) {
++              s->p[i].port.line = find_first_zero_bit(&sc16is7xx_lines,
++                                                      SC16IS7XX_MAX_DEVS);
++              if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
++                      ret = -ERANGE;
++                      goto out_ports;
++              }
++
+               /* Initialize port data */
+               s->p[i].port.dev        = dev;
+               s->p[i].port.irq        = irq;
+@@ -1553,14 +1547,8 @@ static int sc16is7xx_probe(struct device
+               s->p[i].port.rs485_supported = sc16is7xx_rs485_supported;
+               s->p[i].port.ops        = &sc16is7xx_ops;
+               s->p[i].old_mctrl       = 0;
+-              s->p[i].port.line       = sc16is7xx_alloc_line();
+               s->p[i].regmap          = regmaps[i];
+-              if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
+-                      ret = -ENOMEM;
+-                      goto out_ports;
+-              }
+-
+               mutex_init(&s->p[i].efr_lock);
+               ret = uart_get_rs485_mode(&s->p[i].port);
+@@ -1578,8 +1566,13 @@ static int sc16is7xx_probe(struct device
+               kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
+               kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
+               kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc);
++
+               /* Register port */
+-              uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
++              ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
++              if (ret)
++                      goto out_ports;
++
++              set_bit(s->p[i].port.line, &sc16is7xx_lines);
+               /* Enable EFR */
+               sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
+@@ -1646,10 +1639,9 @@ static int sc16is7xx_probe(struct device
+ #endif
+ out_ports:
+-      for (i--; i >= 0; i--) {
+-              uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+-              clear_bit(s->p[i].port.line, &sc16is7xx_lines);
+-      }
++      for (i = 0; i < devtype->nr_uart; i++)
++              if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines))
++                      uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+       kthread_stop(s->kworker_task);
+@@ -1671,8 +1663,8 @@ static void sc16is7xx_remove(struct devi
+       for (i = 0; i < s->devtype->nr_uart; i++) {
+               kthread_cancel_delayed_work_sync(&s->p[i].ms_work);
+-              uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+-              clear_bit(s->p[i].port.line, &sc16is7xx_lines);
++              if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines))
++                      uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+               sc16is7xx_power(&s->p[i].port, 0);
+       }
diff --git a/queue-6.1/serial-sc16is7xx-improve-do-while-loop-in-sc16is7xx_irq.patch b/queue-6.1/serial-sc16is7xx-improve-do-while-loop-in-sc16is7xx_irq.patch
new file mode 100644 (file)
index 0000000..11086dd
--- /dev/null
@@ -0,0 +1,49 @@
+From d5078509c8b06c5c472a60232815e41af81c6446 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Thu, 21 Dec 2023 18:18:12 -0500
+Subject: serial: sc16is7xx: improve do/while loop in sc16is7xx_irq()
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit d5078509c8b06c5c472a60232815e41af81c6446 upstream.
+
+Simplify and improve readability by replacing while(1) loop with
+do {} while, and by using the keep_polling variable as the exit
+condition, making it more explicit.
+
+Fixes: 834449872105 ("sc16is7xx: Fix for multi-channel stall")
+Cc:  <stable@vger.kernel.org>
+Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231221231823.2327894-6-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -784,17 +784,18 @@ out_port_irq:
+ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
+ {
++      bool keep_polling;
++
+       struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
+-      while (1) {
+-              bool keep_polling = false;
++      do {
+               int i;
++              keep_polling = false;
++
+               for (i = 0; i < s->devtype->nr_uart; ++i)
+                       keep_polling |= sc16is7xx_port_irq(s, i);
+-              if (!keep_polling)
+-                      break;
+-      }
++      } while (keep_polling);
+       return IRQ_HANDLED;
+ }
diff --git a/queue-6.1/serial-sc16is7xx-improve-regmap-debugfs-by-using-one-regmap-per-port.patch b/queue-6.1/serial-sc16is7xx-improve-regmap-debugfs-by-using-one-regmap-per-port.patch
new file mode 100644 (file)
index 0000000..9a1db9c
--- /dev/null
@@ -0,0 +1,437 @@
+From 3837a0379533aabb9e4483677077479f7c6aa910 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 30 Oct 2023 17:14:47 -0400
+Subject: serial: sc16is7xx: improve regmap debugfs by using one regmap per port
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit 3837a0379533aabb9e4483677077479f7c6aa910 upstream.
+
+With this current driver regmap implementation, it is hard to make sense
+of the register addresses displayed using the regmap debugfs interface,
+because they do not correspond to the actual register addresses documented
+in the datasheet. For example, register 1 is displayed as registers 04 thru
+07:
+
+$ cat /sys/kernel/debug/regmap/spi0.0/registers
+  04: 10 -> Port 0, register offset 1
+  05: 10 -> Port 1, register offset 1
+  06: 00 -> Port 2, register offset 1 -> invalid
+  07: 00 -> port 3, register offset 1 -> invalid
+  ...
+
+The reason is that bits 0 and 1 of the register address correspond to the
+channel (port) bits, so the register address itself starts at bit 2, and we
+must 'mentally' shift each register address by 2 bits to get its real
+address/offset.
+
+Also, only channels 0 and 1 are supported by the chip, so channel mask
+combinations of 10b and 11b are invalid, and the display of these
+registers is useless.
+
+This patch adds a separate regmap configuration for each port, similar to
+what is done in the max310x driver, so that register addresses displayed
+match the register addresses in the chip datasheet. Also, each port now has
+its own debugfs entry.
+
+Example with new regmap implementation:
+
+$ cat /sys/kernel/debug/regmap/spi0.0-port0/registers
+1: 10
+2: 01
+3: 00
+...
+
+$ cat /sys/kernel/debug/regmap/spi0.0-port1/registers
+1: 10
+2: 01
+3: 00
+
+As an added bonus, this also simplifies some operations (read/write/modify)
+because it is no longer necessary to manually shift register addresses.
+
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |  143 +++++++++++++++++++++++------------------
+ 1 file changed, 81 insertions(+), 62 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -301,8 +301,8 @@
+ /* Misc definitions */
++#define SC16IS7XX_SPI_READ_BIT                BIT(7)
+ #define SC16IS7XX_FIFO_SIZE           (64)
+-#define SC16IS7XX_REG_SHIFT           2
+ #define SC16IS7XX_GPIOS_PER_BANK      4
+ struct sc16is7xx_devtype {
+@@ -324,6 +324,7 @@ struct sc16is7xx_one_config {
+ struct sc16is7xx_one {
+       struct uart_port                port;
+       u8                              line;
++      struct regmap                   *regmap;
+       struct kthread_work             tx_work;
+       struct kthread_work             reg_work;
+       struct kthread_delayed_work     ms_work;
+@@ -362,48 +363,37 @@ static void sc16is7xx_stop_tx(struct uar
+ #define to_sc16is7xx_port(p,e)        ((container_of((p), struct sc16is7xx_port, e)))
+ #define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
+-static int sc16is7xx_line(struct uart_port *port)
+-{
+-      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-
+-      return one->line;
+-}
+-
+ static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
+ {
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       unsigned int val = 0;
+-      const u8 line = sc16is7xx_line(port);
+-      regmap_read(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line, &val);
++      regmap_read(one->regmap, reg, &val);
+       return val;
+ }
+ static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val)
+ {
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+-      const u8 line = sc16is7xx_line(port);
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-      regmap_write(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line, val);
++      regmap_write(one->regmap, reg, val);
+ }
+ static void sc16is7xx_fifo_read(struct uart_port *port, unsigned int rxlen)
+ {
+       struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+-      const u8 line = sc16is7xx_line(port);
+-      u8 addr = (SC16IS7XX_RHR_REG << SC16IS7XX_REG_SHIFT) | line;
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-      regcache_cache_bypass(s->regmap, true);
+-      regmap_raw_read(s->regmap, addr, s->buf, rxlen);
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, true);
++      regmap_raw_read(one->regmap, SC16IS7XX_RHR_REG, s->buf, rxlen);
++      regcache_cache_bypass(one->regmap, false);
+ }
+ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send)
+ {
+       struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+-      const u8 line = sc16is7xx_line(port);
+-      u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line;
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       /*
+        * Don't send zero-length data, at least on SPI it confuses the chip
+@@ -412,19 +402,17 @@ static void sc16is7xx_fifo_write(struct
+       if (unlikely(!to_send))
+               return;
+-      regcache_cache_bypass(s->regmap, true);
+-      regmap_raw_write(s->regmap, addr, s->buf, to_send);
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, true);
++      regmap_raw_write(one->regmap, SC16IS7XX_THR_REG, s->buf, to_send);
++      regcache_cache_bypass(one->regmap, false);
+ }
+ static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
+                                 u8 mask, u8 val)
+ {
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+-      const u8 line = sc16is7xx_line(port);
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-      regmap_update_bits(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line,
+-                         mask, val);
++      regmap_update_bits(one->regmap, reg, mask, val);
+ }
+ static int sc16is7xx_alloc_line(void)
+@@ -479,7 +467,7 @@ static const struct sc16is7xx_devtype sc
+ static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg)
+ {
+-      switch (reg >> SC16IS7XX_REG_SHIFT) {
++      switch (reg) {
+       case SC16IS7XX_RHR_REG:
+       case SC16IS7XX_IIR_REG:
+       case SC16IS7XX_LSR_REG:
+@@ -498,7 +486,7 @@ static bool sc16is7xx_regmap_volatile(st
+ static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg)
+ {
+-      switch (reg >> SC16IS7XX_REG_SHIFT) {
++      switch (reg) {
+       case SC16IS7XX_RHR_REG:
+               return true;
+       default:
+@@ -511,6 +499,7 @@ static bool sc16is7xx_regmap_precious(st
+ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
+ {
+       struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
++      struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       u8 lcr;
+       u8 prescaler = 0;
+       unsigned long clk = port->uartclk, div = clk / 16 / baud;
+@@ -542,12 +531,12 @@ static int sc16is7xx_set_baud(struct uar
+                            SC16IS7XX_LCR_CONF_MODE_B);
+       /* Enable enhanced features */
+-      regcache_cache_bypass(s->regmap, true);
++      regcache_cache_bypass(one->regmap, true);
+       sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
+                             SC16IS7XX_EFR_ENABLE_BIT,
+                             SC16IS7XX_EFR_ENABLE_BIT);
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, false);
+       /* Put LCR back to the normal mode */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+@@ -563,10 +552,10 @@ static int sc16is7xx_set_baud(struct uar
+                            SC16IS7XX_LCR_CONF_MODE_A);
+       /* Write the new divisor */
+-      regcache_cache_bypass(s->regmap, true);
++      regcache_cache_bypass(one->regmap, true);
+       sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
+       sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, false);
+       /* Put LCR back to the normal mode */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+@@ -1094,7 +1083,7 @@ static void sc16is7xx_set_termios(struct
+                            SC16IS7XX_LCR_CONF_MODE_B);
+       /* Configure flow control */
+-      regcache_cache_bypass(s->regmap, true);
++      regcache_cache_bypass(one->regmap, true);
+       sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
+       sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
+@@ -1113,7 +1102,7 @@ static void sc16is7xx_set_termios(struct
+                             SC16IS7XX_EFR_REG,
+                             SC16IS7XX_EFR_FLOWCTRL_BITS,
+                             flow);
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, false);
+       /* Update LCR register */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+@@ -1164,7 +1153,6 @@ static int sc16is7xx_config_rs485(struct
+ static int sc16is7xx_startup(struct uart_port *port)
+ {
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+-      struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+       unsigned int val;
+       unsigned long flags;
+@@ -1181,7 +1169,7 @@ static int sc16is7xx_startup(struct uart
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+                            SC16IS7XX_LCR_CONF_MODE_B);
+-      regcache_cache_bypass(s->regmap, true);
++      regcache_cache_bypass(one->regmap, true);
+       /* Enable write access to enhanced features and internal clock div */
+       sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
+@@ -1199,7 +1187,7 @@ static int sc16is7xx_startup(struct uart
+                            SC16IS7XX_TCR_RX_RESUME(24) |
+                            SC16IS7XX_TCR_RX_HALT(48));
+-      regcache_cache_bypass(s->regmap, false);
++      regcache_cache_bypass(one->regmap, false);
+       /* Now, initialize the UART */
+       sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8);
+@@ -1457,7 +1445,7 @@ static int sc16is7xx_setup_mctrl_ports(s
+       if (s->mctrl_mask)
+               regmap_update_bits(
+                       s->regmap,
+-                      SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
++                      SC16IS7XX_IOCONTROL_REG,
+                       SC16IS7XX_IOCONTROL_MODEM_A_BIT |
+                       SC16IS7XX_IOCONTROL_MODEM_B_BIT, s->mctrl_mask);
+@@ -1472,7 +1460,7 @@ static const struct serial_rs485 sc16is7
+ static int sc16is7xx_probe(struct device *dev,
+                          const struct sc16is7xx_devtype *devtype,
+-                         struct regmap *regmap, int irq)
++                         struct regmap *regmaps[], int irq)
+ {
+       unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
+       unsigned int val;
+@@ -1480,16 +1468,16 @@ static int sc16is7xx_probe(struct device
+       int i, ret;
+       struct sc16is7xx_port *s;
+-      if (IS_ERR(regmap))
+-              return PTR_ERR(regmap);
++      for (i = 0; i < devtype->nr_uart; i++)
++              if (IS_ERR(regmaps[i]))
++                      return PTR_ERR(regmaps[i]);
+       /*
+        * This device does not have an identification register that would
+        * tell us if we are really connected to the correct device.
+        * The best we can do is to check if communication is at all possible.
+        */
+-      ret = regmap_read(regmap,
+-                        SC16IS7XX_LSR_REG << SC16IS7XX_REG_SHIFT, &val);
++      ret = regmap_read(regmaps[0], SC16IS7XX_LSR_REG, &val);
+       if (ret < 0)
+               return -EPROBE_DEFER;
+@@ -1523,7 +1511,7 @@ static int sc16is7xx_probe(struct device
+                       return -EINVAL;
+       }
+-      s->regmap = regmap;
++      s->regmap = regmaps[0];
+       s->devtype = devtype;
+       dev_set_drvdata(dev, s);
+       mutex_init(&s->efr_lock);
+@@ -1538,8 +1526,8 @@ static int sc16is7xx_probe(struct device
+       sched_set_fifo(s->kworker_task);
+       /* reset device, purging any pending irq / data */
+-      regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
+-                      SC16IS7XX_IOCONTROL_SRESET_BIT);
++      regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG,
++                   SC16IS7XX_IOCONTROL_SRESET_BIT);
+       for (i = 0; i < devtype->nr_uart; ++i) {
+               s->p[i].line            = i;
+@@ -1563,6 +1551,7 @@ static int sc16is7xx_probe(struct device
+               s->p[i].port.ops        = &sc16is7xx_ops;
+               s->p[i].old_mctrl       = 0;
+               s->p[i].port.line       = sc16is7xx_alloc_line();
++              s->p[i].regmap          = regmaps[i];
+               if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
+                       ret = -ENOMEM;
+@@ -1591,13 +1580,13 @@ static int sc16is7xx_probe(struct device
+               sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
+                                    SC16IS7XX_LCR_CONF_MODE_B);
+-              regcache_cache_bypass(s->regmap, true);
++              regcache_cache_bypass(regmaps[i], true);
+               /* Enable write access to enhanced features */
+               sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG,
+                                    SC16IS7XX_EFR_ENABLE_BIT);
+-              regcache_cache_bypass(s->regmap, false);
++              regcache_cache_bypass(regmaps[i], false);
+               /* Restore access to general registers */
+               sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00);
+@@ -1700,19 +1689,36 @@ static const struct of_device_id __maybe
+ MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids);
+ static struct regmap_config regcfg = {
+-      .reg_bits = 7,
+-      .pad_bits = 1,
++      .reg_bits = 5,
++      .pad_bits = 3,
+       .val_bits = 8,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = sc16is7xx_regmap_volatile,
+       .precious_reg = sc16is7xx_regmap_precious,
++      .max_register = SC16IS7XX_EFCR_REG,
+ };
++static const char *sc16is7xx_regmap_name(unsigned int port_id)
++{
++      static char buf[6];
++
++      snprintf(buf, sizeof(buf), "port%d", port_id);
++
++      return buf;
++}
++
++static unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id)
++{
++      /* CH1,CH0 are at bits 2:1. */
++      return port_id << 1;
++}
++
+ #ifdef CONFIG_SERIAL_SC16IS7XX_SPI
+ static int sc16is7xx_spi_probe(struct spi_device *spi)
+ {
+       const struct sc16is7xx_devtype *devtype;
+-      struct regmap *regmap;
++      struct regmap *regmaps[2];
++      unsigned int i;
+       int ret;
+       /* Setup SPI bus */
+@@ -1737,11 +1743,20 @@ static int sc16is7xx_spi_probe(struct sp
+               devtype = (struct sc16is7xx_devtype *)id_entry->driver_data;
+       }
+-      regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
+-                            (devtype->nr_uart - 1);
+-      regmap = devm_regmap_init_spi(spi, &regcfg);
++      for (i = 0; i < devtype->nr_uart; i++) {
++              regcfg.name = sc16is7xx_regmap_name(i);
++              /*
++               * If read_flag_mask is 0, the regmap code sets it to a default
++               * of 0x80. Since we specify our own mask, we must add the READ
++               * bit ourselves:
++               */
++              regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) |
++                      SC16IS7XX_SPI_READ_BIT;
++              regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i);
++              regmaps[i] = devm_regmap_init_spi(spi, &regcfg);
++      }
+-      return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq);
++      return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq);
+ }
+ static void sc16is7xx_spi_remove(struct spi_device *spi)
+@@ -1780,7 +1795,8 @@ static int sc16is7xx_i2c_probe(struct i2
+                              const struct i2c_device_id *id)
+ {
+       const struct sc16is7xx_devtype *devtype;
+-      struct regmap *regmap;
++      struct regmap *regmaps[2];
++      unsigned int i;
+       if (i2c->dev.of_node) {
+               devtype = device_get_match_data(&i2c->dev);
+@@ -1790,11 +1806,14 @@ static int sc16is7xx_i2c_probe(struct i2
+               devtype = (struct sc16is7xx_devtype *)id->driver_data;
+       }
+-      regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
+-                            (devtype->nr_uart - 1);
+-      regmap = devm_regmap_init_i2c(i2c, &regcfg);
++      for (i = 0; i < devtype->nr_uart; i++) {
++              regcfg.name = sc16is7xx_regmap_name(i);
++              regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i);
++              regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i);
++              regmaps[i] = devm_regmap_init_i2c(i2c, &regcfg);
++      }
+-      return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq);
++      return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq);
+ }
+ static void sc16is7xx_i2c_remove(struct i2c_client *client)
diff --git a/queue-6.1/serial-sc16is7xx-remove-global-regmap-from-struct-sc16is7xx_port.patch b/queue-6.1/serial-sc16is7xx-remove-global-regmap-from-struct-sc16is7xx_port.patch
new file mode 100644 (file)
index 0000000..dd1ecb0
--- /dev/null
@@ -0,0 +1,90 @@
+From f6959c5217bd799bcb770b95d3c09b3244e175c6 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 11 Dec 2023 12:13:49 -0500
+Subject: serial: sc16is7xx: remove global regmap from struct sc16is7xx_port
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit f6959c5217bd799bcb770b95d3c09b3244e175c6 upstream.
+
+Remove global struct regmap so that it is more obvious that this
+regmap is to be used only in the probe function.
+
+Also add a comment to that effect in probe function.
+
+Fixes: 3837a0379533 ("serial: sc16is7xx: improve regmap debugfs by using one regmap per port")
+Cc:  <stable@vger.kernel.org>
+Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231211171353.2901416-3-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -335,7 +335,6 @@ struct sc16is7xx_one {
+ struct sc16is7xx_port {
+       const struct sc16is7xx_devtype  *devtype;
+-      struct regmap                   *regmap;
+       struct clk                      *clk;
+ #ifdef CONFIG_GPIOLIB
+       struct gpio_chip                gpio;
+@@ -1415,7 +1414,8 @@ static int sc16is7xx_setup_gpio_chip(str
+ /*
+  * Configure ports designated to operate as modem control lines.
+  */
+-static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s)
++static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s,
++                                     struct regmap *regmap)
+ {
+       int i;
+       int ret;
+@@ -1444,7 +1444,7 @@ static int sc16is7xx_setup_mctrl_ports(s
+       if (s->mctrl_mask)
+               regmap_update_bits(
+-                      s->regmap,
++                      regmap,
+                       SC16IS7XX_IOCONTROL_REG,
+                       SC16IS7XX_IOCONTROL_MODEM_A_BIT |
+                       SC16IS7XX_IOCONTROL_MODEM_B_BIT, s->mctrl_mask);
+@@ -1476,6 +1476,10 @@ static int sc16is7xx_probe(struct device
+        * This device does not have an identification register that would
+        * tell us if we are really connected to the correct device.
+        * The best we can do is to check if communication is at all possible.
++       *
++       * Note: regmap[0] is used in the probe function to access registers
++       * common to all channels/ports, as it is guaranteed to be present on
++       * all variants.
+        */
+       ret = regmap_read(regmaps[0], SC16IS7XX_LSR_REG, &val);
+       if (ret < 0)
+@@ -1511,7 +1515,6 @@ static int sc16is7xx_probe(struct device
+                       return -EINVAL;
+       }
+-      s->regmap = regmaps[0];
+       s->devtype = devtype;
+       dev_set_drvdata(dev, s);
+       mutex_init(&s->efr_lock);
+@@ -1526,7 +1529,7 @@ static int sc16is7xx_probe(struct device
+       sched_set_fifo(s->kworker_task);
+       /* reset device, purging any pending irq / data */
+-      regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG,
++      regmap_write(regmaps[0], SC16IS7XX_IOCONTROL_REG,
+                    SC16IS7XX_IOCONTROL_SRESET_BIT);
+       for (i = 0; i < devtype->nr_uart; ++i) {
+@@ -1606,7 +1609,7 @@ static int sc16is7xx_probe(struct device
+                               s->p[u].irda_mode = true;
+       }
+-      ret = sc16is7xx_setup_mctrl_ports(s);
++      ret = sc16is7xx_setup_mctrl_ports(s, regmaps[0]);
+       if (ret)
+               goto out_ports;
diff --git a/queue-6.1/serial-sc16is7xx-remove-obsolete-loop-in-sc16is7xx_port_irq.patch b/queue-6.1/serial-sc16is7xx-remove-obsolete-loop-in-sc16is7xx_port_irq.patch
new file mode 100644 (file)
index 0000000..9408169
--- /dev/null
@@ -0,0 +1,130 @@
+From ed647256e8f226241ecff7baaecdb8632ffc7ec1 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Thu, 21 Dec 2023 18:18:11 -0500
+Subject: serial: sc16is7xx: remove obsolete loop in sc16is7xx_port_irq()
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit ed647256e8f226241ecff7baaecdb8632ffc7ec1 upstream.
+
+Commit 834449872105 ("sc16is7xx: Fix for multi-channel stall") changed
+sc16is7xx_port_irq() from looping multiple times when there was still
+interrupts to serve. It simply changed the do {} while(1) loop to a
+do {} while(0) loop, which makes the loop itself now obsolete.
+
+Clean the code by removing this obsolete do {} while(0) loop.
+
+Fixes: 834449872105 ("sc16is7xx: Fix for multi-channel stall")
+Cc:  <stable@vger.kernel.org>
+Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231221231823.2327894-5-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   89 +++++++++++++++++++----------------------
+ 1 file changed, 43 insertions(+), 46 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -726,58 +726,55 @@ static void sc16is7xx_update_mlines(stru
+ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
+ {
+       bool rc = true;
++      unsigned int iir, rxlen;
+       struct uart_port *port = &s->p[portno].port;
+       struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+       mutex_lock(&one->efr_lock);
+-      do {
+-              unsigned int iir, rxlen;
+-
+-              iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
+-              if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
+-                      rc = false;
+-                      goto out_port_irq;
+-              }
+-
+-              iir &= SC16IS7XX_IIR_ID_MASK;
+-
+-              switch (iir) {
+-              case SC16IS7XX_IIR_RDI_SRC:
+-              case SC16IS7XX_IIR_RLSE_SRC:
+-              case SC16IS7XX_IIR_RTOI_SRC:
+-              case SC16IS7XX_IIR_XOFFI_SRC:
+-                      rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
+-
+-                      /*
+-                       * There is a silicon bug that makes the chip report a
+-                       * time-out interrupt but no data in the FIFO. This is
+-                       * described in errata section 18.1.4.
+-                       *
+-                       * When this happens, read one byte from the FIFO to
+-                       * clear the interrupt.
+-                       */
+-                      if (iir == SC16IS7XX_IIR_RTOI_SRC && !rxlen)
+-                              rxlen = 1;
+-
+-                      if (rxlen)
+-                              sc16is7xx_handle_rx(port, rxlen, iir);
+-                      break;
++      iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
++      if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
++              rc = false;
++              goto out_port_irq;
++      }
++
++      iir &= SC16IS7XX_IIR_ID_MASK;
++
++      switch (iir) {
++      case SC16IS7XX_IIR_RDI_SRC:
++      case SC16IS7XX_IIR_RLSE_SRC:
++      case SC16IS7XX_IIR_RTOI_SRC:
++      case SC16IS7XX_IIR_XOFFI_SRC:
++              rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
++
++              /*
++               * There is a silicon bug that makes the chip report a
++               * time-out interrupt but no data in the FIFO. This is
++               * described in errata section 18.1.4.
++               *
++               * When this happens, read one byte from the FIFO to
++               * clear the interrupt.
++               */
++              if (iir == SC16IS7XX_IIR_RTOI_SRC && !rxlen)
++                      rxlen = 1;
++
++              if (rxlen)
++                      sc16is7xx_handle_rx(port, rxlen, iir);
++              break;
+               /* CTSRTS interrupt comes only when CTS goes inactive */
+-              case SC16IS7XX_IIR_CTSRTS_SRC:
+-              case SC16IS7XX_IIR_MSI_SRC:
+-                      sc16is7xx_update_mlines(one);
+-                      break;
+-              case SC16IS7XX_IIR_THRI_SRC:
+-                      sc16is7xx_handle_tx(port);
+-                      break;
+-              default:
+-                      dev_err_ratelimited(port->dev,
+-                                          "ttySC%i: Unexpected interrupt: %x",
+-                                          port->line, iir);
+-                      break;
+-              }
+-      } while (0);
++      case SC16IS7XX_IIR_CTSRTS_SRC:
++      case SC16IS7XX_IIR_MSI_SRC:
++              sc16is7xx_update_mlines(one);
++              break;
++      case SC16IS7XX_IIR_THRI_SRC:
++              sc16is7xx_handle_tx(port);
++              break;
++      default:
++              dev_err_ratelimited(port->dev,
++                                  "ttySC%i: Unexpected interrupt: %x",
++                                  port->line, iir);
++              break;
++      }
+ out_port_irq:
+       mutex_unlock(&one->efr_lock);
diff --git a/queue-6.1/serial-sc16is7xx-remove-unused-line-structure-member.patch b/queue-6.1/serial-sc16is7xx-remove-unused-line-structure-member.patch
new file mode 100644 (file)
index 0000000..ffff3e0
--- /dev/null
@@ -0,0 +1,39 @@
+From 41a308cbedb2a68a6831f0f2e992e296c4b8aff0 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 11 Dec 2023 12:13:50 -0500
+Subject: serial: sc16is7xx: remove unused line structure member
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit 41a308cbedb2a68a6831f0f2e992e296c4b8aff0 upstream.
+
+Now that the driver has been converted to use one regmap per port, the line
+structure member is no longer used, so remove it.
+
+Fixes: 3837a0379533 ("serial: sc16is7xx: improve regmap debugfs by using one regmap per port")
+Cc:  <stable@vger.kernel.org>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231211171353.2901416-4-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |    2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -323,7 +323,6 @@ struct sc16is7xx_one_config {
+ struct sc16is7xx_one {
+       struct uart_port                port;
+-      u8                              line;
+       struct regmap                   *regmap;
+       struct kthread_work             tx_work;
+       struct kthread_work             reg_work;
+@@ -1533,7 +1532,6 @@ static int sc16is7xx_probe(struct device
+                    SC16IS7XX_IOCONTROL_SRESET_BIT);
+       for (i = 0; i < devtype->nr_uart; ++i) {
+-              s->p[i].line            = i;
+               /* Initialize port data */
+               s->p[i].port.dev        = dev;
+               s->p[i].port.irq        = irq;
diff --git a/queue-6.1/serial-sc16is7xx-remove-wasteful-static-buffer-in-sc16is7xx_regmap_name.patch b/queue-6.1/serial-sc16is7xx-remove-wasteful-static-buffer-in-sc16is7xx_regmap_name.patch
new file mode 100644 (file)
index 0000000..71a8f0f
--- /dev/null
@@ -0,0 +1,53 @@
+From 6bcab3c8acc88e265c570dea969fd04f137c8a4c Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 11 Dec 2023 12:13:48 -0500
+Subject: serial: sc16is7xx: remove wasteful static buffer in sc16is7xx_regmap_name()
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+commit 6bcab3c8acc88e265c570dea969fd04f137c8a4c upstream.
+
+Using a static buffer inside sc16is7xx_regmap_name() was a convenient and
+simple way to set the regmap name without having to allocate and free a
+buffer each time it is called. The drawback is that the static buffer
+wastes memory for nothing once regmap is fully initialized.
+
+Remove static buffer and use constant strings instead.
+
+This also avoids a truncation warning when using "%d" or "%u" in snprintf
+which was flagged by kernel test robot.
+
+Fixes: 3837a0379533 ("serial: sc16is7xx: improve regmap debugfs by using one regmap per port")
+Cc:  <stable@vger.kernel.org> # 6.1.x: 3837a03 serial: sc16is7xx: improve regmap debugfs by using one regmap per port
+Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20231211171353.2901416-2-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c |   14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -1698,13 +1698,15 @@ static struct regmap_config regcfg = {
+       .max_register = SC16IS7XX_EFCR_REG,
+ };
+-static const char *sc16is7xx_regmap_name(unsigned int port_id)
++static const char *sc16is7xx_regmap_name(u8 port_id)
+ {
+-      static char buf[6];
+-
+-      snprintf(buf, sizeof(buf), "port%d", port_id);
+-
+-      return buf;
++      switch (port_id) {
++      case 0: return "port0";
++      case 1: return "port1";
++      default:
++              WARN_ON(true);
++              return NULL;
++      }
+ }
+ static unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id)
index 38bfbaba177e418468be53d8d5c99bf71f1105de..50764bbf20879a1c9efcd841788174dc5b89ec21 100644 (file)
@@ -54,3 +54,15 @@ rtc-add-support-for-configuring-the-uip-timeout-for-rtc-reads.patch
 rtc-extend-timeout-for-waiting-for-uip-to-clear-to-1s.patch
 nouveau-vmm-don-t-set-addr-on-the-fail-path-to-avoid-warning.patch
 ubifs-ubifs_symlink-fix-memleak-of-inode-i_link-in-error-path.patch
+mm-rmap-fix-misplaced-parenthesis-of-a-likely.patch
+mm-sparsemem-fix-race-in-accessing-memory_section-usage.patch
+rename-fix-the-locking-of-subdirectories.patch
+serial-sc16is7xx-improve-regmap-debugfs-by-using-one-regmap-per-port.patch
+serial-sc16is7xx-remove-wasteful-static-buffer-in-sc16is7xx_regmap_name.patch
+serial-sc16is7xx-remove-global-regmap-from-struct-sc16is7xx_port.patch
+serial-sc16is7xx-remove-unused-line-structure-member.patch
+serial-sc16is7xx-change-efr-lock-to-operate-on-each-channels.patch
+serial-sc16is7xx-convert-from-_raw_-to-_noinc_-regmap-functions-for-fifo.patch
+serial-sc16is7xx-fix-invalid-sc16is7xx_lines-bitfield-in-case-of-probe-error.patch
+serial-sc16is7xx-remove-obsolete-loop-in-sc16is7xx_port_irq.patch
+serial-sc16is7xx-improve-do-while-loop-in-sc16is7xx_irq.patch