--- /dev/null
+From 7bb943806ff61e83ae4cceef8906b7fe52453e8a Mon Sep 17 00:00:00 2001
+From: James Gowans <jgowans@amazon.com>
+Date: Wed, 13 Dec 2023 08:40:04 +0200
+Subject: kexec: do syscore_shutdown() in kernel_kexec
+
+From: James Gowans <jgowans@amazon.com>
+
+commit 7bb943806ff61e83ae4cceef8906b7fe52453e8a upstream.
+
+syscore_shutdown() runs driver and module callbacks to get the system into
+a state where it can be correctly shut down. In commit 6f389a8f1dd2 ("PM
+/ reboot: call syscore_shutdown() after disable_nonboot_cpus()")
+syscore_shutdown() was removed from kernel_restart_prepare() and hence got
+(incorrectly?) removed from the kexec flow. This was innocuous until
+commit 6735150b6997 ("KVM: Use syscore_ops instead of reboot_notifier to
+hook restart/shutdown") changed the way that KVM registered its shutdown
+callbacks, switching from reboot notifiers to syscore_ops.shutdown. As
+syscore_shutdown() is missing from kexec, KVM's shutdown hook is not run
+and virtualisation is left enabled on the boot CPU which results in triple
+faults when switching to the new kernel on Intel x86 VT-x with VMXE
+enabled.
+
+Fix this by adding syscore_shutdown() to the kexec sequence. In terms of
+where to add it, it is being added after migrating the kexec task to the
+boot CPU, but before APs are shut down. It is not totally clear if this
+is the best place: in commit 6f389a8f1dd2 ("PM / reboot: call
+syscore_shutdown() after disable_nonboot_cpus()") it is stated that
+"syscore_ops operations should be carried with one CPU on-line and
+interrupts disabled." APs are only offlined later in machine_shutdown(),
+so this syscore_shutdown() is being run while APs are still online. This
+seems to be the correct place as it matches where syscore_shutdown() is
+run in the reboot and halt flows - they also run it before APs are shut
+down. The assumption is that the commit message in commit 6f389a8f1dd2
+("PM / reboot: call syscore_shutdown() after disable_nonboot_cpus()") is
+no longer valid.
+
+KVM has been discussed here as it is what broke loudly by not having
+syscore_shutdown() in kexec, but this change impacts more than just KVM;
+all drivers/modules which register a syscore_ops.shutdown callback will
+now be invoked in the kexec flow. Looking at some of them like x86 MCE it
+is probably more correct to also shut these down during kexec.
+Maintainers of all drivers which use syscore_ops.shutdown are added on CC
+for visibility. They are:
+
+arch/powerpc/platforms/cell/spu_base.c .shutdown = spu_shutdown,
+arch/x86/kernel/cpu/mce/core.c .shutdown = mce_syscore_shutdown,
+arch/x86/kernel/i8259.c .shutdown = i8259A_shutdown,
+drivers/irqchip/irq-i8259.c .shutdown = i8259A_shutdown,
+drivers/irqchip/irq-sun6i-r.c .shutdown = sun6i_r_intc_shutdown,
+drivers/leds/trigger/ledtrig-cpu.c .shutdown = ledtrig_cpu_syscore_shutdown,
+drivers/power/reset/sc27xx-poweroff.c .shutdown = sc27xx_poweroff_shutdown,
+kernel/irq/generic-chip.c .shutdown = irq_gc_shutdown,
+virt/kvm/kvm_main.c .shutdown = kvm_shutdown,
+
+This has been tested by doing a kexec on x86_64 and aarch64.
+
+Link: https://lkml.kernel.org/r/20231213064004.2419447-1-jgowans@amazon.com
+Fixes: 6735150b6997 ("KVM: Use syscore_ops instead of reboot_notifier to hook restart/shutdown")
+Signed-off-by: James Gowans <jgowans@amazon.com>
+Cc: Baoquan He <bhe@redhat.com>
+Cc: Eric Biederman <ebiederm@xmission.com>
+Cc: Paolo Bonzini <pbonzini@redhat.com>
+Cc: Sean Christopherson <seanjc@google.com>
+Cc: Marc Zyngier <maz@kernel.org>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Tony Luck <tony.luck@intel.com>
+Cc: Borislav Petkov <bp@alien8.de>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Cc: Ingo Molnar <mingo@redhat.com>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Jernej Skrabec <jernej.skrabec@gmail.com>
+Cc: Samuel Holland <samuel@sholland.org>
+Cc: Pavel Machek <pavel@ucw.cz>
+Cc: Sebastian Reichel <sre@kernel.org>
+Cc: Orson Zhai <orsonzhai@gmail.com>
+Cc: Alexander Graf <graf@amazon.de>
+Cc: Jan H. Schoenherr <jschoenh@amazon.de>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/kexec_core.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/kernel/kexec_core.c
++++ b/kernel/kexec_core.c
+@@ -1271,6 +1271,7 @@ int kernel_kexec(void)
+ kexec_in_progress = true;
+ kernel_restart_prepare("kexec reboot");
+ migrate_to_reboot_cpu();
++ syscore_shutdown();
+
+ /*
+ * migrate_to_reboot_cpu() disables CPU hotplug assuming that
--- /dev/null
+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
+@@ -261,8 +261,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);
--- /dev/null
+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
+@@ -1774,6 +1774,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
+@@ -1967,7 +1968,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)
+@@ -1991,6 +1992,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
+@@ -2004,13 +2006,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
+@@ -792,6 +792,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
+@@ -799,16 +806,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;
+ }
+
+ /*
--- /dev/null
+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
+@@ -101,7 +101,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
+@@ -123,6 +123,9 @@ get_offset_ctx no
+ 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
+@@ -1045,3 +1045,21 @@ filesystem type is now moved to a later
+ As this is a VFS level change it has no practical consequences for filesystems
+ other than that all of them must use one of the provided kill_litter_super(),
+ kill_anon_super(), or kill_block_super() helpers.
++
++---
++
++**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
+@@ -3021,20 +3021,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;
+ }
+
+ /*
+@@ -4733,11 +4727,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
+@@ -4769,6 +4764,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;
+@@ -4822,15 +4818,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)))
+@@ -4878,8 +4891,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) {
--- /dev/null
+From 00bcfcd47a52f50f07a2e88d730d7931384cb073 Mon Sep 17 00:00:00 2001
+From: Donet Tom <donettom@linux.vnet.ibm.com>
+Date: Wed, 10 Jan 2024 14:03:35 +0530
+Subject: selftests: mm: hugepage-vmemmap fails on 64K page size systems
+
+From: Donet Tom <donettom@linux.vnet.ibm.com>
+
+commit 00bcfcd47a52f50f07a2e88d730d7931384cb073 upstream.
+
+The kernel sefltest mm/hugepage-vmemmap fails on architectures which has
+different page size other than 4K. In hugepage-vmemmap page size used is
+4k so the pfn calculation will go wrong on systems which has different
+page size .The length of MAP_HUGETLB memory must be hugepage aligned but
+in hugepage-vmemmap map length is 2M so this will not get aligned if the
+system has differnet hugepage size.
+
+Added psize() to get the page size and default_huge_page_size() to
+get the default hugepage size at run time, hugepage-vmemmap test pass
+on powerpc with 64K page size and x86 with 4K page size.
+
+Result on powerpc without patch (page size 64K)
+*# ./hugepage-vmemmap
+Returned address is 0x7effff000000 whose pfn is 0
+Head page flags (100000000) is invalid
+check_page_flags: Invalid argument
+*#
+
+Result on powerpc with patch (page size 64K)
+*# ./hugepage-vmemmap
+Returned address is 0x7effff000000 whose pfn is 600
+*#
+
+Result on x86 with patch (page size 4K)
+*# ./hugepage-vmemmap
+Returned address is 0x7fc7c2c00000 whose pfn is 1dac00
+*#
+
+Link: https://lkml.kernel.org/r/3b3a3ae37ba21218481c482a872bbf7526031600.1704865754.git.donettom@linux.vnet.ibm.com
+Fixes: b147c89cd429 ("selftests: vm: add a hugetlb test case")
+Signed-off-by: Donet Tom <donettom@linux.vnet.ibm.com>
+Reported-by: Geetika Moolchandani <geetika@linux.ibm.com>
+Tested-by: Geetika Moolchandani <geetika@linux.ibm.com>
+Acked-by: Muchun Song <muchun.song@linux.dev>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ tools/testing/selftests/mm/hugepage-vmemmap.c | 29 ++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 11 deletions(-)
+
+--- a/tools/testing/selftests/mm/hugepage-vmemmap.c
++++ b/tools/testing/selftests/mm/hugepage-vmemmap.c
+@@ -10,10 +10,7 @@
+ #include <unistd.h>
+ #include <sys/mman.h>
+ #include <fcntl.h>
+-
+-#define MAP_LENGTH (2UL * 1024 * 1024)
+-
+-#define PAGE_SIZE 4096
++#include "vm_util.h"
+
+ #define PAGE_COMPOUND_HEAD (1UL << 15)
+ #define PAGE_COMPOUND_TAIL (1UL << 16)
+@@ -39,6 +36,9 @@
+ #define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)
+ #endif
+
++static size_t pagesize;
++static size_t maplength;
++
+ static void write_bytes(char *addr, size_t length)
+ {
+ unsigned long i;
+@@ -56,7 +56,7 @@ static unsigned long virt_to_pfn(void *a
+ if (fd < 0)
+ return -1UL;
+
+- lseek(fd, (unsigned long)addr / PAGE_SIZE * sizeof(pagemap), SEEK_SET);
++ lseek(fd, (unsigned long)addr / pagesize * sizeof(pagemap), SEEK_SET);
+ read(fd, &pagemap, sizeof(pagemap));
+ close(fd);
+
+@@ -86,7 +86,7 @@ static int check_page_flags(unsigned lon
+ * this also verifies kernel has correctly set the fake page_head to tail
+ * while hugetlb_free_vmemmap is enabled.
+ */
+- for (i = 1; i < MAP_LENGTH / PAGE_SIZE; i++) {
++ for (i = 1; i < maplength / pagesize; i++) {
+ read(fd, &pageflags, sizeof(pageflags));
+ if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS ||
+ (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) {
+@@ -106,18 +106,25 @@ int main(int argc, char **argv)
+ void *addr;
+ unsigned long pfn;
+
+- addr = mmap(MAP_ADDR, MAP_LENGTH, PROT_READ | PROT_WRITE, MAP_FLAGS, -1, 0);
++ pagesize = psize();
++ maplength = default_huge_page_size();
++ if (!maplength) {
++ printf("Unable to determine huge page size\n");
++ exit(1);
++ }
++
++ addr = mmap(MAP_ADDR, maplength, PROT_READ | PROT_WRITE, MAP_FLAGS, -1, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ /* Trigger allocation of HugeTLB page. */
+- write_bytes(addr, MAP_LENGTH);
++ write_bytes(addr, maplength);
+
+ pfn = virt_to_pfn(addr);
+ if (pfn == -1UL) {
+- munmap(addr, MAP_LENGTH);
++ munmap(addr, maplength);
+ perror("virt_to_pfn");
+ exit(1);
+ }
+@@ -125,13 +132,13 @@ int main(int argc, char **argv)
+ printf("Returned address is %p whose pfn is %lx\n", addr, pfn);
+
+ if (check_page_flags(pfn) < 0) {
+- munmap(addr, MAP_LENGTH);
++ munmap(addr, maplength);
+ perror("check_page_flags");
+ exit(1);
+ }
+
+ /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */
+- if (munmap(addr, MAP_LENGTH)) {
++ if (munmap(addr, maplength)) {
+ perror("munmap");
+ exit(1);
+ }
--- /dev/null
+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,
+@@ -707,11 +706,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;
+@@ -737,15 +735,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;
+
+@@ -785,15 +788,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;
+@@ -804,24 +809,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);
+@@ -928,9 +931,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);
+ }
+@@ -1014,7 +1017,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;
+@@ -1073,7 +1075,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);
+@@ -1103,7 +1105,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,
+@@ -1514,7 +1516,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,
+@@ -1557,6 +1558,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;
--- /dev/null
+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);
+@@ -1697,6 +1698,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,
+ };
+
--- /dev/null
+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,
+@@ -1532,6 +1519,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;
+@@ -1551,14 +1545,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);
+@@ -1576,8 +1564,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,
+@@ -1644,10 +1637,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);
+
+@@ -1669,8 +1661,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);
+ }
+
--- /dev/null
+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
+@@ -782,17 +782,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;
+ }
--- /dev/null
+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);
+@@ -1092,7 +1081,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]);
+
+@@ -1111,7 +1100,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);
+@@ -1162,7 +1151,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;
+
+@@ -1179,7 +1167,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,
+@@ -1197,7 +1185,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);
+@@ -1455,7 +1443,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);
+
+@@ -1470,7 +1458,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;
+@@ -1478,16 +1466,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;
+
+@@ -1521,7 +1509,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);
+@@ -1536,8 +1524,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;
+@@ -1561,6 +1549,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;
+@@ -1589,13 +1578,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);
+@@ -1698,19 +1687,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 */
+@@ -1735,11 +1741,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, ®cfg);
++ 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, ®cfg);
++ }
+
+- 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)
+@@ -1778,7 +1793,8 @@ static int sc16is7xx_i2c_probe(struct i2
+ {
+ const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
+ 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);
+@@ -1788,11 +1804,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, ®cfg);
++ 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, ®cfg);
++ }
+
+- 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)
--- /dev/null
+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;
+@@ -1413,7 +1412,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;
+@@ -1442,7 +1442,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);
+@@ -1474,6 +1474,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)
+@@ -1509,7 +1513,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);
+@@ -1524,7 +1527,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) {
+@@ -1604,7 +1607,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;
+
--- /dev/null
+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
+@@ -724,58 +724,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);
--- /dev/null
+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;
+@@ -1531,7 +1530,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;
--- /dev/null
+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
+@@ -1696,13 +1696,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)
nouveau-vmm-don-t-set-addr-on-the-fail-path-to-avoid-warning.patch
efi-disable-mirror-feature-during-crashkernel.patch
ubifs-ubifs_symlink-fix-memleak-of-inode-i_link-in-error-path.patch
+kexec-do-syscore_shutdown-in-kernel_kexec.patch
+selftests-mm-hugepage-vmemmap-fails-on-64k-page-size-systems.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