--- /dev/null
+From d143460613d4f5c352e9fd397f083421e249ffe6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jan 2024 21:47:32 +0530
+Subject: genirq/irqdomain: Add DOMAIN_BUS_DEVICE_MSI
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+[ Upstream commit 6516d5a295356f8fd5827a1c0954d7ed5b2324dd ]
+
+Add a new domain bus token to prepare for device MSI which aims to replace
+the existing platform MSI maze.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Anup Patel <apatel@ventanamicro.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Link: https://lore.kernel.org/r/20240127161753.114685-5-apatel@ventanamicro.com
+Stable-dep-of: 64506b3d23a3 ("scsi: ufs: qcom: Only free platform MSIs when ESI is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/irqdomain_defs.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h
+index c29921fd8cd15..a7dea0c8c5e0c 100644
+--- a/include/linux/irqdomain_defs.h
++++ b/include/linux/irqdomain_defs.h
+@@ -26,6 +26,7 @@ enum irq_domain_bus_token {
+ DOMAIN_BUS_DMAR,
+ DOMAIN_BUS_AMDVI,
+ DOMAIN_BUS_PCI_DEVICE_IMS,
++ DOMAIN_BUS_DEVICE_MSI,
+ };
+
+ #endif /* _LINUX_IRQDOMAIN_DEFS_H */
+--
+2.43.0
+
--- /dev/null
+From 5df23ec861a0208ef524a27e44c694ca2decb7ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jan 2024 21:47:34 +0530
+Subject: irqchip: Convert all platform MSI users to the new API
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+[ Upstream commit 14fd06c776b5289a43c91cdc64bac3bdbc7b397e ]
+
+Switch all the users of the platform MSI domain over to invoke the new
+interfaces which branch to the original platform MSI functions when the
+irqdomain associated to the caller device does not yet provide MSI parent
+functionality.
+
+No functional change.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Anup Patel <apatel@ventanamicro.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Link: https://lore.kernel.org/r/20240127161753.114685-7-apatel@ventanamicro.com
+Stable-dep-of: 64506b3d23a3 ("scsi: ufs: qcom: Only free platform MSIs when ESI is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma/mv_xor_v2.c | 8 ++++----
+ drivers/dma/qcom/hidma.c | 6 +++---
+ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 5 +++--
+ drivers/mailbox/bcm-flexrm-mailbox.c | 8 ++++----
+ drivers/perf/arm_smmuv3_pmu.c | 4 ++--
+ drivers/ufs/host/ufs-qcom.c | 8 ++++----
+ 6 files changed, 20 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c
+index 0e1e9ca1c005a..cd2b9a6ab621d 100644
+--- a/drivers/dma/mv_xor_v2.c
++++ b/drivers/dma/mv_xor_v2.c
+@@ -747,8 +747,8 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
+ if (IS_ERR(xor_dev->clk))
+ return PTR_ERR(xor_dev->clk);
+
+- ret = platform_msi_domain_alloc_irqs(&pdev->dev, 1,
+- mv_xor_v2_set_msi_msg);
++ ret = platform_device_msi_init_and_alloc_irqs(&pdev->dev, 1,
++ mv_xor_v2_set_msi_msg);
+ if (ret)
+ return ret;
+
+@@ -851,7 +851,7 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
+ xor_dev->desc_size * MV_XOR_V2_DESC_NUM,
+ xor_dev->hw_desq_virt, xor_dev->hw_desq);
+ free_msi_irqs:
+- platform_msi_domain_free_irqs(&pdev->dev);
++ platform_device_msi_free_irqs_all(&pdev->dev);
+ return ret;
+ }
+
+@@ -867,7 +867,7 @@ static int mv_xor_v2_remove(struct platform_device *pdev)
+
+ devm_free_irq(&pdev->dev, xor_dev->irq, xor_dev);
+
+- platform_msi_domain_free_irqs(&pdev->dev);
++ platform_device_msi_free_irqs_all(&pdev->dev);
+
+ tasklet_kill(&xor_dev->irq_tasklet);
+
+diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
+index 834ae519c15de..f2b299c23b1e8 100644
+--- a/drivers/dma/qcom/hidma.c
++++ b/drivers/dma/qcom/hidma.c
+@@ -696,7 +696,7 @@ static void hidma_free_msis(struct hidma_dev *dmadev)
+ devm_free_irq(dev, virq, &dmadev->lldev);
+ }
+
+- platform_msi_domain_free_irqs(dev);
++ platform_device_msi_free_irqs_all(dev);
+ #endif
+ }
+
+@@ -706,8 +706,8 @@ static int hidma_request_msi(struct hidma_dev *dmadev,
+ #ifdef CONFIG_GENERIC_MSI_IRQ
+ int rc, i, virq;
+
+- rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
+- hidma_write_msi_msg);
++ rc = platform_device_msi_init_and_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
++ hidma_write_msi_msg);
+ if (rc)
+ return rc;
+
+diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+index 68b81f9c2f4b1..435eee52e033a 100644
+--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+@@ -3141,7 +3141,8 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
+ static void arm_smmu_free_msis(void *data)
+ {
+ struct device *dev = data;
+- platform_msi_domain_free_irqs(dev);
++
++ platform_device_msi_free_irqs_all(dev);
+ }
+
+ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+@@ -3182,7 +3183,7 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
+ }
+
+ /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
+- ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
++ ret = platform_device_msi_init_and_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
+ if (ret) {
+ dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n");
+ return;
+diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c
+index a2b8839d4e7c5..7094d44869a8e 100644
+--- a/drivers/mailbox/bcm-flexrm-mailbox.c
++++ b/drivers/mailbox/bcm-flexrm-mailbox.c
+@@ -1587,8 +1587,8 @@ static int flexrm_mbox_probe(struct platform_device *pdev)
+ }
+
+ /* Allocate platform MSIs for each ring */
+- ret = platform_msi_domain_alloc_irqs(dev, mbox->num_rings,
+- flexrm_mbox_msi_write);
++ ret = platform_device_msi_init_and_alloc_irqs(dev, mbox->num_rings,
++ flexrm_mbox_msi_write);
+ if (ret)
+ goto fail_destroy_cmpl_pool;
+
+@@ -1641,7 +1641,7 @@ static int flexrm_mbox_probe(struct platform_device *pdev)
+
+ fail_free_debugfs_root:
+ debugfs_remove_recursive(mbox->root);
+- platform_msi_domain_free_irqs(dev);
++ platform_device_msi_free_irqs_all(dev);
+ fail_destroy_cmpl_pool:
+ dma_pool_destroy(mbox->cmpl_pool);
+ fail_destroy_bd_pool:
+@@ -1657,7 +1657,7 @@ static int flexrm_mbox_remove(struct platform_device *pdev)
+
+ debugfs_remove_recursive(mbox->root);
+
+- platform_msi_domain_free_irqs(dev);
++ platform_device_msi_free_irqs_all(dev);
+
+ dma_pool_destroy(mbox->cmpl_pool);
+ dma_pool_destroy(mbox->bd_pool);
+diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c
+index 31e491e7f2065..2946422539fb7 100644
+--- a/drivers/perf/arm_smmuv3_pmu.c
++++ b/drivers/perf/arm_smmuv3_pmu.c
+@@ -719,7 +719,7 @@ static void smmu_pmu_free_msis(void *data)
+ {
+ struct device *dev = data;
+
+- platform_msi_domain_free_irqs(dev);
++ platform_device_msi_free_irqs_all(dev);
+ }
+
+ static void smmu_pmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+@@ -749,7 +749,7 @@ static void smmu_pmu_setup_msi(struct smmu_pmu *pmu)
+ if (!(readl(pmu->reg_base + SMMU_PMCG_CFGR) & SMMU_PMCG_CFGR_MSI))
+ return;
+
+- ret = platform_msi_domain_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg);
++ ret = platform_device_msi_init_and_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg);
+ if (ret) {
+ dev_warn(dev, "failed to allocate MSIs\n");
+ return;
+diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
+index 0a914fd44494d..d77cfb2ab1acd 100644
+--- a/drivers/ufs/host/ufs-qcom.c
++++ b/drivers/ufs/host/ufs-qcom.c
+@@ -1816,8 +1816,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
+ * 2. Poll queues do not need ESI.
+ */
+ nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
+- ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs,
+- ufs_qcom_write_msi_msg);
++ ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
++ ufs_qcom_write_msi_msg);
+ if (ret) {
+ dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret);
+ goto out;
+@@ -1846,7 +1846,7 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
+ devm_free_irq(hba->dev, desc->irq, hba);
+ }
+ msi_unlock_descs(hba->dev);
+- platform_msi_domain_free_irqs(hba->dev);
++ platform_device_msi_free_irqs_all(hba->dev);
+ } else {
+ if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 &&
+ host->hw_ver.step == 0) {
+@@ -1926,7 +1926,7 @@ static void ufs_qcom_remove(struct platform_device *pdev)
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+- platform_msi_domain_free_irqs(hba->dev);
++ platform_device_msi_free_irqs_all(hba->dev);
+ }
+
+ static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
+--
+2.43.0
+
--- /dev/null
+From 5ed5249f8a5b1f12689d57fea3f66ab1baebce5a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Oct 2023 02:17:43 -0700
+Subject: mempolicy: fix migrate_pages(2) syscall return nr_failed
+
+From: Hugh Dickins <hughd@google.com>
+
+[ Upstream commit 1cb5d11a370f661c5d0d888bb0cfc2cdc5791382 ]
+
+"man 2 migrate_pages" says "On success migrate_pages() returns the number
+of pages that could not be moved". Although 5.3 and 5.4 commits fixed
+mbind(MPOL_MF_STRICT|MPOL_MF_MOVE*) to fail with EIO when not all pages
+could be moved (because some could not be isolated for migration),
+migrate_pages(2) was left still reporting only those pages failing at the
+migration stage, forgetting those failing at the earlier isolation stage.
+
+Fix that by accumulating a long nr_failed count in struct queue_pages,
+returned by queue_pages_range() when it's not returning an error, for
+adding on to the nr_failed count from migrate_pages() in mm/migrate.c. A
+count of pages? It's more a count of folios, but changing it to pages
+would entail more work (also in mm/migrate.c): does not seem justified.
+
+queue_pages_range() itself should only return -EIO in the "strictly
+unmovable" case (STRICT without any MOVEs): in that case it's best to
+break out as soon as nr_failed gets set; but otherwise it should continue
+to isolate pages for MOVing even when nr_failed - as the mbind(2) manpage
+promises.
+
+There's a case when nr_failed should be incremented when it was missed:
+queue_folios_pte_range() and queue_folios_hugetlb() count the transient
+migration entries, like queue_folios_pmd() already did. And there's a
+case when nr_failed should not be incremented when it would have been: in
+meeting later PTEs of the same large folio, which can only be isolated
+once: fixed by recording the current large folio in struct queue_pages.
+
+Clean up the affected functions, fixing or updating many comments. Bool
+migrate_folio_add(), without -EIO: true if adding, or if skipping shared
+(but its arguable folio_estimated_sharers() heuristic left unchanged).
+Use MPOL_MF_WRLOCK flag to queue_pages_range(), instead of bool lock_vma.
+Use explicit STRICT|MOVE* flags where queue_pages_test_walk() checks for
+skipping, instead of hiding them behind MPOL_MF_VALID.
+
+Link: https://lkml.kernel.org/r/9a6b0b9-3bb-dbef-8adf-efab4397b8d@google.com
+Signed-off-by: Hugh Dickins <hughd@google.com>
+Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
+Reviewed-by: "Huang, Ying" <ying.huang@intel.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Christoph Lameter <cl@linux.com>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
+Cc: Mel Gorman <mgorman@techsingularity.net>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Kravetz <mike.kravetz@oracle.com>
+Cc: Nhat Pham <nphamcs@gmail.com>
+Cc: Sidhartha Kumar <sidhartha.kumar@oracle.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Tejun heo <tj@kernel.org>
+Cc: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Cc: Yang Shi <shy828301@gmail.com>
+Cc: Yosry Ahmed <yosryahmed@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 091c1dd2d4df ("mm/mempolicy: fix migrate_to_node() assuming there is at least one VMA in a MM")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 338 +++++++++++++++++++++++--------------------------
+ 1 file changed, 159 insertions(+), 179 deletions(-)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 109826a2af387..54f1b78d1b2c0 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -111,7 +111,8 @@
+
+ /* Internal flags */
+ #define MPOL_MF_DISCONTIG_OK (MPOL_MF_INTERNAL << 0) /* Skip checks for continuous vmas */
+-#define MPOL_MF_INVERT (MPOL_MF_INTERNAL << 1) /* Invert check for nodemask */
++#define MPOL_MF_INVERT (MPOL_MF_INTERNAL << 1) /* Invert check for nodemask */
++#define MPOL_MF_WRLOCK (MPOL_MF_INTERNAL << 2) /* Write-lock walked vmas */
+
+ static struct kmem_cache *policy_cache;
+ static struct kmem_cache *sn_cache;
+@@ -420,9 +421,19 @@ static const struct mempolicy_operations mpol_ops[MPOL_MAX] = {
+ },
+ };
+
+-static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
++static bool migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags);
+
++static bool strictly_unmovable(unsigned long flags)
++{
++ /*
++ * STRICT without MOVE flags lets do_mbind() fail immediately with -EIO
++ * if any misplaced page is found.
++ */
++ return (flags & (MPOL_MF_STRICT | MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ==
++ MPOL_MF_STRICT;
++}
++
+ struct queue_pages {
+ struct list_head *pagelist;
+ unsigned long flags;
+@@ -430,7 +441,8 @@ struct queue_pages {
+ unsigned long start;
+ unsigned long end;
+ struct vm_area_struct *first;
+- bool has_unmovable;
++ struct folio *large; /* note last large folio encountered */
++ long nr_failed; /* could not be isolated at this time */
+ };
+
+ /*
+@@ -448,61 +460,37 @@ static inline bool queue_folio_required(struct folio *folio,
+ return node_isset(nid, *qp->nmask) == !(flags & MPOL_MF_INVERT);
+ }
+
+-/*
+- * queue_folios_pmd() has three possible return values:
+- * 0 - folios are placed on the right node or queued successfully, or
+- * special page is met, i.e. zero page, or unmovable page is found
+- * but continue walking (indicated by queue_pages.has_unmovable).
+- * -EIO - is migration entry or only MPOL_MF_STRICT was specified and an
+- * existing folio was already on a node that does not follow the
+- * policy.
+- */
+-static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr,
+- unsigned long end, struct mm_walk *walk)
+- __releases(ptl)
++static void queue_folios_pmd(pmd_t *pmd, struct mm_walk *walk)
+ {
+- int ret = 0;
+ struct folio *folio;
+ struct queue_pages *qp = walk->private;
+- unsigned long flags;
+
+ if (unlikely(is_pmd_migration_entry(*pmd))) {
+- ret = -EIO;
+- goto unlock;
++ qp->nr_failed++;
++ return;
+ }
+ folio = pfn_folio(pmd_pfn(*pmd));
+ if (is_huge_zero_page(&folio->page)) {
+ walk->action = ACTION_CONTINUE;
+- goto unlock;
++ return;
+ }
+ if (!queue_folio_required(folio, qp))
+- goto unlock;
+-
+- flags = qp->flags;
+- /* go to folio migration */
+- if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+- if (!vma_migratable(walk->vma) ||
+- migrate_folio_add(folio, qp->pagelist, flags)) {
+- qp->has_unmovable = true;
+- goto unlock;
+- }
+- } else
+- ret = -EIO;
+-unlock:
+- spin_unlock(ptl);
+- return ret;
++ return;
++ if (!(qp->flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ||
++ !vma_migratable(walk->vma) ||
++ !migrate_folio_add(folio, qp->pagelist, qp->flags))
++ qp->nr_failed++;
+ }
+
+ /*
+- * Scan through pages checking if pages follow certain conditions,
+- * and move them to the pagelist if they do.
++ * Scan through folios, checking if they satisfy the required conditions,
++ * moving them from LRU to local pagelist for migration if they do (or not).
+ *
+- * queue_folios_pte_range() has three possible return values:
+- * 0 - folios are placed on the right node or queued successfully, or
+- * special page is met, i.e. zero page, or unmovable page is found
+- * but continue walking (indicated by queue_pages.has_unmovable).
+- * -EIO - only MPOL_MF_STRICT was specified and an existing folio was already
+- * on a node that does not follow the policy.
++ * queue_folios_pte_range() has two possible return values:
++ * 0 - continue walking to scan for more, even if an existing folio on the
++ * wrong node could not be isolated and queued for migration.
++ * -EIO - only MPOL_MF_STRICT was specified, without MPOL_MF_MOVE or ..._ALL,
++ * and an existing folio was on a node that does not follow the policy.
+ */
+ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ unsigned long end, struct mm_walk *walk)
+@@ -516,8 +504,11 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ spinlock_t *ptl;
+
+ ptl = pmd_trans_huge_lock(pmd, vma);
+- if (ptl)
+- return queue_folios_pmd(pmd, ptl, addr, end, walk);
++ if (ptl) {
++ queue_folios_pmd(pmd, walk);
++ spin_unlock(ptl);
++ goto out;
++ }
+
+ mapped_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
+ if (!pte) {
+@@ -526,8 +517,13 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ }
+ for (; addr != end; pte++, addr += PAGE_SIZE) {
+ ptent = ptep_get(pte);
+- if (!pte_present(ptent))
++ if (pte_none(ptent))
+ continue;
++ if (!pte_present(ptent)) {
++ if (is_migration_entry(pte_to_swp_entry(ptent)))
++ qp->nr_failed++;
++ continue;
++ }
+ folio = vm_normal_folio(vma, addr, ptent);
+ if (!folio || folio_is_zone_device(folio))
+ continue;
+@@ -539,95 +535,87 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
+ continue;
+ if (!queue_folio_required(folio, qp))
+ continue;
+- if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+- /*
+- * MPOL_MF_STRICT must be specified if we get here.
+- * Continue walking vmas due to MPOL_MF_MOVE* flags.
+- */
+- if (!vma_migratable(vma))
+- qp->has_unmovable = true;
+-
++ if (folio_test_large(folio)) {
+ /*
+- * Do not abort immediately since there may be
+- * temporary off LRU pages in the range. Still
+- * need migrate other LRU pages.
++ * A large folio can only be isolated from LRU once,
++ * but may be mapped by many PTEs (and Copy-On-Write may
++ * intersperse PTEs of other, order 0, folios). This is
++ * a common case, so don't mistake it for failure (but
++ * there can be other cases of multi-mapped pages which
++ * this quick check does not help to filter out - and a
++ * search of the pagelist might grow to be prohibitive).
++ *
++ * migrate_pages(&pagelist) returns nr_failed folios, so
++ * check "large" now so that queue_pages_range() returns
++ * a comparable nr_failed folios. This does imply that
++ * if folio could not be isolated for some racy reason
++ * at its first PTE, later PTEs will not give it another
++ * chance of isolation; but keeps the accounting simple.
+ */
+- if (migrate_folio_add(folio, qp->pagelist, flags))
+- qp->has_unmovable = true;
+- } else
+- break;
++ if (folio == qp->large)
++ continue;
++ qp->large = folio;
++ }
++ if (!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ||
++ !vma_migratable(vma) ||
++ !migrate_folio_add(folio, qp->pagelist, flags)) {
++ qp->nr_failed++;
++ if (strictly_unmovable(flags))
++ break;
++ }
+ }
+ pte_unmap_unlock(mapped_pte, ptl);
+ cond_resched();
+-
+- return addr != end ? -EIO : 0;
++out:
++ if (qp->nr_failed && strictly_unmovable(flags))
++ return -EIO;
++ return 0;
+ }
+
+ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask,
+ unsigned long addr, unsigned long end,
+ struct mm_walk *walk)
+ {
+- int ret = 0;
+ #ifdef CONFIG_HUGETLB_PAGE
+ struct queue_pages *qp = walk->private;
+- unsigned long flags = (qp->flags & MPOL_MF_VALID);
++ unsigned long flags = qp->flags;
+ struct folio *folio;
+ spinlock_t *ptl;
+ pte_t entry;
+
+ ptl = huge_pte_lock(hstate_vma(walk->vma), walk->mm, pte);
+ entry = huge_ptep_get(pte);
+- if (!pte_present(entry))
++ if (!pte_present(entry)) {
++ if (unlikely(is_hugetlb_entry_migration(entry)))
++ qp->nr_failed++;
+ goto unlock;
++ }
+ folio = pfn_folio(pte_pfn(entry));
+ if (!queue_folio_required(folio, qp))
+ goto unlock;
+-
+- if (flags == MPOL_MF_STRICT) {
+- /*
+- * STRICT alone means only detecting misplaced folio and no
+- * need to further check other vma.
+- */
+- ret = -EIO;
++ if (!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ||
++ !vma_migratable(walk->vma)) {
++ qp->nr_failed++;
+ goto unlock;
+ }
+-
+- if (!vma_migratable(walk->vma)) {
+- /*
+- * Must be STRICT with MOVE*, otherwise .test_walk() have
+- * stopped walking current vma.
+- * Detecting misplaced folio but allow migrating folios which
+- * have been queued.
+- */
+- qp->has_unmovable = true;
+- goto unlock;
+- }
+-
+ /*
+- * With MPOL_MF_MOVE, we try to migrate only unshared folios. If it
+- * is shared it is likely not worth migrating.
++ * Unless MPOL_MF_MOVE_ALL, we try to avoid migrating a shared folio.
++ * Choosing not to migrate a shared folio is not counted as a failure.
+ *
+ * To check if the folio is shared, ideally we want to make sure
+ * every page is mapped to the same process. Doing that is very
+- * expensive, so check the estimated mapcount of the folio instead.
++ * expensive, so check the estimated sharers of the folio instead.
+ */
+- if (flags & (MPOL_MF_MOVE_ALL) ||
+- (flags & MPOL_MF_MOVE && folio_estimated_sharers(folio) == 1 &&
+- !hugetlb_pmd_shared(pte))) {
+- if (!isolate_hugetlb(folio, qp->pagelist) &&
+- (flags & MPOL_MF_STRICT))
+- /*
+- * Failed to isolate folio but allow migrating pages
+- * which have been queued.
+- */
+- qp->has_unmovable = true;
+- }
++ if ((flags & MPOL_MF_MOVE_ALL) ||
++ (folio_estimated_sharers(folio) == 1 && !hugetlb_pmd_shared(pte)))
++ if (!isolate_hugetlb(folio, qp->pagelist))
++ qp->nr_failed++;
+ unlock:
+ spin_unlock(ptl);
+-#else
+- BUG();
++ if (qp->nr_failed && strictly_unmovable(flags))
++ return -EIO;
+ #endif
+- return ret;
++ return 0;
+ }
+
+ #ifdef CONFIG_NUMA_BALANCING
+@@ -708,8 +696,11 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
+ return 1;
+ }
+
+- /* queue pages from current vma */
+- if (flags & MPOL_MF_VALID)
++ /*
++ * Check page nodes, and queue pages to move, in the current vma.
++ * But if no moving, and no strict checking, the scan can be skipped.
++ */
++ if (flags & (MPOL_MF_STRICT | MPOL_MF_MOVE | MPOL_MF_MOVE_ALL))
+ return 0;
+ return 1;
+ }
+@@ -731,22 +722,21 @@ static const struct mm_walk_ops queue_pages_lock_vma_walk_ops = {
+ /*
+ * Walk through page tables and collect pages to be migrated.
+ *
+- * If pages found in a given range are on a set of nodes (determined by
+- * @nodes and @flags,) it's isolated and queued to the pagelist which is
+- * passed via @private.
++ * If pages found in a given range are not on the required set of @nodes,
++ * and migration is allowed, they are isolated and queued to @pagelist.
+ *
+- * queue_pages_range() has three possible return values:
+- * 1 - there is unmovable page, but MPOL_MF_MOVE* & MPOL_MF_STRICT were
+- * specified.
+- * 0 - queue pages successfully or no misplaced page.
+- * errno - i.e. misplaced pages with MPOL_MF_STRICT specified (-EIO) or
+- * memory range specified by nodemask and maxnode points outside
+- * your accessible address space (-EFAULT)
++ * queue_pages_range() may return:
++ * 0 - all pages already on the right node, or successfully queued for moving
++ * (or neither strict checking nor moving requested: only range checking).
++ * >0 - this number of misplaced folios could not be queued for moving
++ * (a hugetlbfs page or a transparent huge page being counted as 1).
++ * -EIO - a misplaced page found, when MPOL_MF_STRICT specified without MOVEs.
++ * -EFAULT - a hole in the memory range, when MPOL_MF_DISCONTIG_OK unspecified.
+ */
+-static int
++static long
+ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
+ nodemask_t *nodes, unsigned long flags,
+- struct list_head *pagelist, bool lock_vma)
++ struct list_head *pagelist)
+ {
+ int err;
+ struct queue_pages qp = {
+@@ -756,20 +746,17 @@ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
+ .start = start,
+ .end = end,
+ .first = NULL,
+- .has_unmovable = false,
+ };
+- const struct mm_walk_ops *ops = lock_vma ?
++ const struct mm_walk_ops *ops = (flags & MPOL_MF_WRLOCK) ?
+ &queue_pages_lock_vma_walk_ops : &queue_pages_walk_ops;
+
+ err = walk_page_range(mm, start, end, ops, &qp);
+
+- if (qp.has_unmovable)
+- err = 1;
+ if (!qp.first)
+ /* whole range in hole */
+ err = -EFAULT;
+
+- return err;
++ return err ? : qp.nr_failed;
+ }
+
+ /*
+@@ -1032,16 +1019,16 @@ static long do_get_mempolicy(int *policy, nodemask_t *nmask,
+ }
+
+ #ifdef CONFIG_MIGRATION
+-static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
++static bool migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags)
+ {
+ /*
+- * We try to migrate only unshared folios. If it is shared it
+- * is likely not worth migrating.
++ * Unless MPOL_MF_MOVE_ALL, we try to avoid migrating a shared folio.
++ * Choosing not to migrate a shared folio is not counted as a failure.
+ *
+ * To check if the folio is shared, ideally we want to make sure
+ * every page is mapped to the same process. Doing that is very
+- * expensive, so check the estimated mapcount of the folio instead.
++ * expensive, so check the estimated sharers of the folio instead.
+ */
+ if ((flags & MPOL_MF_MOVE_ALL) || folio_estimated_sharers(folio) == 1) {
+ if (folio_isolate_lru(folio)) {
+@@ -1049,32 +1036,31 @@ static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ node_stat_mod_folio(folio,
+ NR_ISOLATED_ANON + folio_is_file_lru(folio),
+ folio_nr_pages(folio));
+- } else if (flags & MPOL_MF_STRICT) {
++ } else {
+ /*
+ * Non-movable folio may reach here. And, there may be
+ * temporary off LRU folios or non-LRU movable folios.
+ * Treat them as unmovable folios since they can't be
+- * isolated, so they can't be moved at the moment. It
+- * should return -EIO for this case too.
++ * isolated, so they can't be moved at the moment.
+ */
+- return -EIO;
++ return false;
+ }
+ }
+-
+- return 0;
++ return true;
+ }
+
+ /*
+ * Migrate pages from one node to a target node.
+ * Returns error or the number of pages not migrated.
+ */
+-static int migrate_to_node(struct mm_struct *mm, int source, int dest,
+- int flags)
++static long migrate_to_node(struct mm_struct *mm, int source, int dest,
++ int flags)
+ {
+ nodemask_t nmask;
+ struct vm_area_struct *vma;
+ LIST_HEAD(pagelist);
+- int err = 0;
++ long nr_failed;
++ long err = 0;
+ struct migration_target_control mtc = {
+ .nid = dest,
+ .gfp_mask = GFP_HIGHUSER_MOVABLE | __GFP_THISNODE,
+@@ -1083,23 +1069,27 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
+ nodes_clear(nmask);
+ node_set(source, nmask);
+
++ VM_BUG_ON(!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)));
++ vma = find_vma(mm, 0);
++
+ /*
+- * This does not "check" the range but isolates all pages that
++ * This does not migrate the range, but isolates all pages that
+ * need migration. Between passing in the full user address
+- * space range and MPOL_MF_DISCONTIG_OK, this call can not fail.
++ * space range and MPOL_MF_DISCONTIG_OK, this call cannot fail,
++ * but passes back the count of pages which could not be isolated.
+ */
+- vma = find_vma(mm, 0);
+- VM_BUG_ON(!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)));
+- queue_pages_range(mm, vma->vm_start, mm->task_size, &nmask,
+- flags | MPOL_MF_DISCONTIG_OK, &pagelist, false);
++ nr_failed = queue_pages_range(mm, vma->vm_start, mm->task_size, &nmask,
++ flags | MPOL_MF_DISCONTIG_OK, &pagelist);
+
+ if (!list_empty(&pagelist)) {
+ err = migrate_pages(&pagelist, alloc_migration_target, NULL,
+- (unsigned long)&mtc, MIGRATE_SYNC, MR_SYSCALL, NULL);
++ (unsigned long)&mtc, MIGRATE_SYNC, MR_SYSCALL, NULL);
+ if (err)
+ putback_movable_pages(&pagelist);
+ }
+
++ if (err >= 0)
++ err += nr_failed;
+ return err;
+ }
+
+@@ -1112,8 +1102,8 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
+ int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+ const nodemask_t *to, int flags)
+ {
+- int busy = 0;
+- int err = 0;
++ long nr_failed = 0;
++ long err = 0;
+ nodemask_t tmp;
+
+ lru_cache_disable();
+@@ -1195,7 +1185,7 @@ int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+ node_clear(source, tmp);
+ err = migrate_to_node(mm, source, dest, flags);
+ if (err > 0)
+- busy += err;
++ nr_failed += err;
+ if (err < 0)
+ break;
+ }
+@@ -1204,8 +1194,7 @@ int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+ lru_cache_enable();
+ if (err < 0)
+ return err;
+- return busy;
+-
++ return (nr_failed < INT_MAX) ? nr_failed : INT_MAX;
+ }
+
+ /*
+@@ -1244,10 +1233,10 @@ static struct folio *new_folio(struct folio *src, unsigned long start)
+ }
+ #else
+
+-static int migrate_folio_add(struct folio *folio, struct list_head *foliolist,
++static bool migrate_folio_add(struct folio *folio, struct list_head *foliolist,
+ unsigned long flags)
+ {
+- return -EIO;
++ return false;
+ }
+
+ int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+@@ -1271,8 +1260,8 @@ static long do_mbind(unsigned long start, unsigned long len,
+ struct vma_iterator vmi;
+ struct mempolicy *new;
+ unsigned long end;
+- int err;
+- int ret;
++ long err;
++ long nr_failed;
+ LIST_HEAD(pagelist);
+
+ if (flags & ~(unsigned long)MPOL_MF_VALID)
+@@ -1312,10 +1301,8 @@ static long do_mbind(unsigned long start, unsigned long len,
+ start, start + len, mode, mode_flags,
+ nmask ? nodes_addr(*nmask)[0] : NUMA_NO_NODE);
+
+- if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+-
++ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL))
+ lru_cache_disable();
+- }
+ {
+ NODEMASK_SCRATCH(scratch);
+ if (scratch) {
+@@ -1331,44 +1318,37 @@ static long do_mbind(unsigned long start, unsigned long len,
+ goto mpol_out;
+
+ /*
+- * Lock the VMAs before scanning for pages to migrate, to ensure we don't
+- * miss a concurrently inserted page.
++ * Lock the VMAs before scanning for pages to migrate,
++ * to ensure we don't miss a concurrently inserted page.
+ */
+- ret = queue_pages_range(mm, start, end, nmask,
+- flags | MPOL_MF_INVERT, &pagelist, true);
++ nr_failed = queue_pages_range(mm, start, end, nmask,
++ flags | MPOL_MF_INVERT | MPOL_MF_WRLOCK, &pagelist);
+
+- if (ret < 0) {
+- err = ret;
+- goto up_out;
+- }
+-
+- vma_iter_init(&vmi, mm, start);
+- prev = vma_prev(&vmi);
+- for_each_vma_range(vmi, vma, end) {
+- err = mbind_range(&vmi, vma, &prev, start, end, new);
+- if (err)
+- break;
++ if (nr_failed < 0) {
++ err = nr_failed;
++ } else {
++ vma_iter_init(&vmi, mm, start);
++ prev = vma_prev(&vmi);
++ for_each_vma_range(vmi, vma, end) {
++ err = mbind_range(&vmi, vma, &prev, start, end, new);
++ if (err)
++ break;
++ }
+ }
+
+ if (!err) {
+- int nr_failed = 0;
+-
+ if (!list_empty(&pagelist)) {
+ WARN_ON_ONCE(flags & MPOL_MF_LAZY);
+- nr_failed = migrate_pages(&pagelist, new_folio, NULL,
++ nr_failed |= migrate_pages(&pagelist, new_folio, NULL,
+ start, MIGRATE_SYNC, MR_MEMPOLICY_MBIND, NULL);
+- if (nr_failed)
+- putback_movable_pages(&pagelist);
+ }
+-
+- if (((ret > 0) || nr_failed) && (flags & MPOL_MF_STRICT))
++ if (nr_failed && (flags & MPOL_MF_STRICT))
+ err = -EIO;
+- } else {
+-up_out:
+- if (!list_empty(&pagelist))
+- putback_movable_pages(&pagelist);
+ }
+
++ if (!list_empty(&pagelist))
++ putback_movable_pages(&pagelist);
++
+ mmap_write_unlock(mm);
+ mpol_out:
+ mpol_put(new);
+--
+2.43.0
+
--- /dev/null
+From 2e67827c993ba96be423bdf4728e11e9c1fed90b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 Nov 2024 21:11:51 +0100
+Subject: mm/mempolicy: fix migrate_to_node() assuming there is at least one
+ VMA in a MM
+
+From: David Hildenbrand <david@redhat.com>
+
+[ Upstream commit 091c1dd2d4df6edd1beebe0e5863d4034ade9572 ]
+
+We currently assume that there is at least one VMA in a MM, which isn't
+true.
+
+So we might end up having find_vma() return NULL, to then de-reference
+NULL. So properly handle find_vma() returning NULL.
+
+This fixes the report:
+
+Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN PTI
+KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
+CPU: 1 UID: 0 PID: 6021 Comm: syz-executor284 Not tainted 6.12.0-rc7-syzkaller-00187-gf868cd251776 #0
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/30/2024
+RIP: 0010:migrate_to_node mm/mempolicy.c:1090 [inline]
+RIP: 0010:do_migrate_pages+0x403/0x6f0 mm/mempolicy.c:1194
+Code: ...
+RSP: 0018:ffffc9000375fd08 EFLAGS: 00010246
+RAX: 0000000000000000 RBX: ffffc9000375fd78 RCX: 0000000000000000
+RDX: ffff88807e171300 RSI: dffffc0000000000 RDI: ffff88803390c044
+RBP: ffff88807e171428 R08: 0000000000000014 R09: fffffbfff2039ef1
+R10: ffffffff901cf78f R11: 0000000000000000 R12: 0000000000000003
+R13: ffffc9000375fe90 R14: ffffc9000375fe98 R15: ffffc9000375fdf8
+FS: 00005555919e1380(0000) GS:ffff8880b8700000(0000) knlGS:0000000000000000
+CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+CR2: 00005555919e1ca8 CR3: 000000007f12a000 CR4: 00000000003526f0
+DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+Call Trace:
+ <TASK>
+ kernel_migrate_pages+0x5b2/0x750 mm/mempolicy.c:1709
+ __do_sys_migrate_pages mm/mempolicy.c:1727 [inline]
+ __se_sys_migrate_pages mm/mempolicy.c:1723 [inline]
+ __x64_sys_migrate_pages+0x96/0x100 mm/mempolicy.c:1723
+ do_syscall_x64 arch/x86/entry/common.c:52 [inline]
+ do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+[akpm@linux-foundation.org: add unlikely()]
+Link: https://lkml.kernel.org/r/20241120201151.9518-1-david@redhat.com
+Fixes: 39743889aaf7 ("[PATCH] Swap Migration V5: sys_migrate_pages interface")
+Signed-off-by: David Hildenbrand <david@redhat.com>
+Reported-by: syzbot+3511625422f7aa637f0d@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/lkml/673d2696.050a0220.3c9d61.012f.GAE@google.com/T/
+Reviewed-by: Liam R. Howlett <Liam.Howlett@Oracle.com>
+Reviewed-by: Christoph Lameter <cl@linux.com>
+Cc: Liam R. Howlett <Liam.Howlett@Oracle.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/mempolicy.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index 54f1b78d1b2c0..94c74c594d102 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -1071,6 +1071,10 @@ static long migrate_to_node(struct mm_struct *mm, int source, int dest,
+
+ VM_BUG_ON(!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)));
+ vma = find_vma(mm, 0);
++ if (unlikely(!vma)) {
++ mmap_read_unlock(mm);
++ return 0;
++ }
+
+ /*
+ * This does not migrate the range, but isolates all pages that
+--
+2.43.0
+
--- /dev/null
+From 1c3c977271d7d952c04732d5b0175a8b3d5039a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Jan 2024 21:47:33 +0530
+Subject: platform-msi: Prepare for real per device domains
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+[ Upstream commit c88f9110bfbca5975a8dee4c9792ba12684c7bca ]
+
+Provide functions to create and remove per device MSI domains which replace
+the platform-MSI domains. The new model is that each of the devices which
+utilize platform-MSI gets now its private MSI domain which is "customized"
+in size and with a device specific function to write the MSI message into
+the device.
+
+This is the same functionality as platform-MSI but it avoids all the down
+sides of platform MSI, i.e. the extra ID book keeping, the special data
+structure in the msi descriptor. Further the domains are only created when
+the devices are really in use, so the burden is on the usage and not on the
+infrastructure.
+
+Fill in the domain template and provide two functions to init/allocate and
+remove a per device MSI domain.
+
+Until all users and parent domain providers are converted, the init/alloc
+function invokes the original platform-MSI code when the irqdomain which is
+associated to the device does not provide MSI parent functionality yet.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Anup Patel <apatel@ventanamicro.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Link: https://lore.kernel.org/r/20240127161753.114685-6-apatel@ventanamicro.com
+Stable-dep-of: 64506b3d23a3 ("scsi: ufs: qcom: Only free platform MSIs when ESI is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/base/platform-msi.c | 103 ++++++++++++++++++++++++++++++++++++
+ include/linux/msi.h | 4 ++
+ 2 files changed, 107 insertions(+)
+
+diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
+index f37ad34c80ec4..b56e919acabb9 100644
+--- a/drivers/base/platform-msi.c
++++ b/drivers/base/platform-msi.c
+@@ -13,6 +13,8 @@
+ #include <linux/msi.h>
+ #include <linux/slab.h>
+
++/* Begin of removal area. Once everything is converted over. Cleanup the includes too! */
++
+ #define DEV_ID_SHIFT 21
+ #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
+
+@@ -350,3 +352,104 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir
+
+ return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
+ }
++
++/* End of removal area */
++
++/* Real per device domain interfaces */
++
++/*
++ * This indirection can go when platform_device_msi_init_and_alloc_irqs()
++ * is switched to a proper irq_chip::irq_write_msi_msg() callback. Keep it
++ * simple for now.
++ */
++static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
++{
++ irq_write_msi_msg_t cb = d->chip_data;
++
++ cb(irq_data_get_msi_desc(d), msg);
++}
++
++static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc)
++{
++ arg->desc = desc;
++ arg->hwirq = desc->msi_index;
++}
++
++static const struct msi_domain_template platform_msi_template = {
++ .chip = {
++ .name = "pMSI",
++ .irq_mask = irq_chip_mask_parent,
++ .irq_unmask = irq_chip_unmask_parent,
++ .irq_write_msi_msg = platform_msi_write_msi_msg,
++ /* The rest is filled in by the platform MSI parent */
++ },
++
++ .ops = {
++ .set_desc = platform_msi_set_desc_byindex,
++ },
++
++ .info = {
++ .bus_token = DOMAIN_BUS_DEVICE_MSI,
++ },
++};
++
++/**
++ * platform_device_msi_init_and_alloc_irqs - Initialize platform device MSI
++ * and allocate interrupts for @dev
++ * @dev: The device for which to allocate interrupts
++ * @nvec: The number of interrupts to allocate
++ * @write_msi_msg: Callback to write an interrupt message for @dev
++ *
++ * Returns:
++ * Zero for success, or an error code in case of failure
++ *
++ * This creates a MSI domain on @dev which has @dev->msi.domain as
++ * parent. The parent domain sets up the new domain. The domain has
++ * a fixed size of @nvec. The domain is managed by devres and will
++ * be removed when the device is removed.
++ *
++ * Note: For migration purposes this falls back to the original platform_msi code
++ * up to the point where all platforms have been converted to the MSI
++ * parent model.
++ */
++int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
++ irq_write_msi_msg_t write_msi_msg)
++{
++ struct irq_domain *domain = dev->msi.domain;
++
++ if (!domain || !write_msi_msg)
++ return -EINVAL;
++
++ /* Migration support. Will go away once everything is converted */
++ if (!irq_domain_is_msi_parent(domain))
++ return platform_msi_domain_alloc_irqs(dev, nvec, write_msi_msg);
++
++ /*
++ * @write_msi_msg is stored in the resulting msi_domain_info::data.
++ * The underlying domain creation mechanism will assign that
++ * callback to the resulting irq chip.
++ */
++ if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
++ &platform_msi_template,
++ nvec, NULL, write_msi_msg))
++ return -ENODEV;
++
++ return msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
++}
++EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
++
++/**
++ * platform_device_msi_free_irqs_all - Free all interrupts for @dev
++ * @dev: The device for which to free interrupts
++ */
++void platform_device_msi_free_irqs_all(struct device *dev)
++{
++ struct irq_domain *domain = dev->msi.domain;
++
++ msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
++
++ /* Migration support. Will go away once everything is converted */
++ if (!irq_domain_is_msi_parent(domain))
++ platform_msi_free_priv_data(dev);
++}
++EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
+diff --git a/include/linux/msi.h b/include/linux/msi.h
+index ddace8c34dcf9..fc32c919e2edd 100644
+--- a/include/linux/msi.h
++++ b/include/linux/msi.h
+@@ -656,6 +656,10 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir
+ void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nvec);
+ void *platform_msi_get_host_data(struct irq_domain *domain);
++/* Per device platform MSI */
++int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
++ irq_write_msi_msg_t write_msi_msg);
++void platform_device_msi_free_irqs_all(struct device *dev);
+
+ bool msi_device_has_isolated_msi(struct device *dev);
+ #else /* CONFIG_GENERIC_MSI_IRQ */
+--
+2.43.0
+
--- /dev/null
+From c6f3fffef669e185df7ace84f9dded53ed049b8b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Nov 2024 18:21:46 +0800
+Subject: sched/numa: fix memory leak due to the overwritten vma->numab_state
+
+From: Adrian Huang <ahuang12@lenovo.com>
+
+[ Upstream commit 5f1b64e9a9b7ee9cfd32c6b2fab796e29bfed075 ]
+
+[Problem Description]
+When running the hackbench program of LTP, the following memory leak is
+reported by kmemleak.
+
+ # /opt/ltp/testcases/bin/hackbench 20 thread 1000
+ Running with 20*40 (== 800) tasks.
+
+ # dmesg | grep kmemleak
+ ...
+ kmemleak: 480 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
+ kmemleak: 665 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
+
+ # cat /sys/kernel/debug/kmemleak
+ unreferenced object 0xffff888cd8ca2c40 (size 64):
+ comm "hackbench", pid 17142, jiffies 4299780315
+ hex dump (first 32 bytes):
+ ac 74 49 00 01 00 00 00 4c 84 49 00 01 00 00 00 .tI.....L.I.....
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ backtrace (crc bff18fd4):
+ [<ffffffff81419a89>] __kmalloc_cache_noprof+0x2f9/0x3f0
+ [<ffffffff8113f715>] task_numa_work+0x725/0xa00
+ [<ffffffff8110f878>] task_work_run+0x58/0x90
+ [<ffffffff81ddd9f8>] syscall_exit_to_user_mode+0x1c8/0x1e0
+ [<ffffffff81dd78d5>] do_syscall_64+0x85/0x150
+ [<ffffffff81e0012b>] entry_SYSCALL_64_after_hwframe+0x76/0x7e
+ ...
+
+This issue can be consistently reproduced on three different servers:
+ * a 448-core server
+ * a 256-core server
+ * a 192-core server
+
+[Root Cause]
+Since multiple threads are created by the hackbench program (along with
+the command argument 'thread'), a shared vma might be accessed by two or
+more cores simultaneously. When two or more cores observe that
+vma->numab_state is NULL at the same time, vma->numab_state will be
+overwritten.
+
+Although current code ensures that only one thread scans the VMAs in a
+single 'numa_scan_period', there might be a chance for another thread
+to enter in the next 'numa_scan_period' while we have not gotten till
+numab_state allocation [1].
+
+Note that the command `/opt/ltp/testcases/bin/hackbench 50 process 1000`
+cannot the reproduce the issue. It is verified with 200+ test runs.
+
+[Solution]
+Use the cmpxchg atomic operation to ensure that only one thread executes
+the vma->numab_state assignment.
+
+[1] https://lore.kernel.org/lkml/1794be3c-358c-4cdc-a43d-a1f841d91ef7@amd.com/
+
+Link: https://lkml.kernel.org/r/20241113102146.2384-1-ahuang12@lenovo.com
+Fixes: ef6a22b70f6d ("sched/numa: apply the scan delay to every new vma")
+Signed-off-by: Adrian Huang <ahuang12@lenovo.com>
+Reported-by: Jiwei Sun <sunjw10@lenovo.com>
+Reviewed-by: Raghavendra K T <raghavendra.kt@amd.com>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Ben Segall <bsegall@google.com>
+Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>
+Cc: Ingo Molnar <mingo@redhat.com>
+Cc: Juri Lelli <juri.lelli@redhat.com>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Steven Rostedt <rostedt@goodmis.org>
+Cc: Valentin Schneider <vschneid@redhat.com>
+Cc: Vincent Guittot <vincent.guittot@linaro.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/sched/fair.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
+index 934d6f198b073..ddab19e5bd637 100644
+--- a/kernel/sched/fair.c
++++ b/kernel/sched/fair.c
+@@ -3344,10 +3344,16 @@ static void task_numa_work(struct callback_head *work)
+
+ /* Initialise new per-VMA NUMAB state. */
+ if (!vma->numab_state) {
+- vma->numab_state = kzalloc(sizeof(struct vma_numab_state),
+- GFP_KERNEL);
+- if (!vma->numab_state)
++ struct vma_numab_state *ptr;
++
++ ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
++ if (!ptr)
++ continue;
++
++ if (cmpxchg(&vma->numab_state, NULL, ptr)) {
++ kfree(ptr);
+ continue;
++ }
+
+ vma->numab_state->start_scan_seq = mm->numa_scan_seq;
+
+--
+2.43.0
+
--- /dev/null
+From 364e7e94105819ed30f3ba32ed806e0d06715107 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Oct 2023 21:27:46 +0530
+Subject: sched/numa: Fix mm numa_scan_seq based unconditional scan
+
+From: Raghavendra K T <raghavendra.kt@amd.com>
+
+[ Upstream commit 84db47ca7146d7bd00eb5cf2b93989a971c84650 ]
+
+Since commit fc137c0ddab2 ("sched/numa: enhance vma scanning logic")
+
+NUMA Balancing allows updating PTEs to trap NUMA hinting faults if the
+task had previously accessed VMA. However unconditional scan of VMAs are
+allowed during initial phase of VMA creation until process's
+mm numa_scan_seq reaches 2 even though current task had not accessed VMA.
+
+Rationale:
+ - Without initial scan subsequent PTE update may never happen.
+ - Give fair opportunity to all the VMAs to be scanned and subsequently
+understand the access pattern of all the VMAs.
+
+But it has a corner case where, if a VMA is created after some time,
+process's mm numa_scan_seq could be already greater than 2.
+
+For e.g., values of mm numa_scan_seq when VMAs are created by running
+mmtest autonuma benchmark briefly looks like:
+start_seq=0 : 459
+start_seq=2 : 138
+start_seq=3 : 144
+start_seq=4 : 8
+start_seq=8 : 1
+start_seq=9 : 1
+This results in no unconditional PTE updates for those VMAs created after
+some time.
+
+Fix:
+ - Note down the initial value of mm numa_scan_seq in per VMA start_seq.
+ - Allow unconditional scan till start_seq + 2.
+
+Result:
+SUT: AMD EPYC Milan with 2 NUMA nodes 256 cpus.
+base kernel: upstream 6.6-rc6 with Mels patches [1] applied.
+
+kernbench
+========== base patched %gain
+Amean elsp-128 165.09 ( 0.00%) 164.78 * 0.19%*
+
+Duration User 41404.28 41375.08
+Duration System 9862.22 9768.48
+Duration Elapsed 519.87 518.72
+
+Ops NUMA PTE updates 1041416.00 831536.00
+Ops NUMA hint faults 263296.00 220966.00
+Ops NUMA pages migrated 258021.00 212769.00
+Ops AutoNUMA cost 1328.67 1114.69
+
+autonumabench
+
+NUMA01_THREADLOCAL
+==================
+Amean elsp-NUMA01_THREADLOCAL 81.79 (0.00%) 67.74 * 17.18%*
+
+Duration User 54832.73 47379.67
+Duration System 75.00 185.75
+Duration Elapsed 576.72 476.09
+
+Ops NUMA PTE updates 394429.00 11121044.00
+Ops NUMA hint faults 1001.00 8906404.00
+Ops NUMA pages migrated 288.00 2998694.00
+Ops AutoNUMA cost 7.77 44666.84
+
+Signed-off-by: Raghavendra K T <raghavendra.kt@amd.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Acked-by: Mel Gorman <mgorman@suse.de>
+Link: https://lore.kernel.org/r/2ea7cbce80ac7c62e90cbfb9653a7972f902439f.1697816692.git.raghavendra.kt@amd.com
+Stable-dep-of: 5f1b64e9a9b7 ("sched/numa: fix memory leak due to the overwritten vma->numab_state")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/mm_types.h | 3 +++
+ kernel/sched/fair.c | 4 +++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
+index 43c19d85dfe7f..20c96ce98751a 100644
+--- a/include/linux/mm_types.h
++++ b/include/linux/mm_types.h
+@@ -576,6 +576,9 @@ struct vma_numab_state {
+ */
+ unsigned long pids_active[2];
+
++ /* MM scan sequence ID when scan first started after VMA creation */
++ int start_scan_seq;
++
+ /*
+ * MM scan sequence ID when the VMA was last completely scanned.
+ * A VMA is not eligible for scanning if prev_scan_seq == numa_scan_seq
+diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
+index db59bf549c644..934d6f198b073 100644
+--- a/kernel/sched/fair.c
++++ b/kernel/sched/fair.c
+@@ -3197,7 +3197,7 @@ static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma)
+ * This is also done to avoid any side effect of task scanning
+ * amplifying the unfairness of disjoint set of VMAs' access.
+ */
+- if (READ_ONCE(current->mm->numa_scan_seq) < 2)
++ if ((READ_ONCE(current->mm->numa_scan_seq) - vma->numab_state->start_scan_seq) < 2)
+ return true;
+
+ pids = vma->numab_state->pids_active[0] | vma->numab_state->pids_active[1];
+@@ -3349,6 +3349,8 @@ static void task_numa_work(struct callback_head *work)
+ if (!vma->numab_state)
+ continue;
+
++ vma->numab_state->start_scan_seq = mm->numa_scan_seq;
++
+ vma->numab_state->next_scan = now +
+ msecs_to_jiffies(sysctl_numa_balancing_scan_delay);
+
+--
+2.43.0
+
--- /dev/null
+From 53940a5722e6b2e1963ad33d83f92b8251830b0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 Sep 2023 16:57:22 +0200
+Subject: scsi: ufs: Convert all platform drivers to return void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit 0842b7617e3491f489aff6f84712c388e32c1877 ]
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart from
+emitting a warning) and this typically results in resource leaks. To
+improve here there is a quest to make the remove callback return void. In
+the first step of this quest all drivers are converted to .remove_new()
+which already returns void. Eventually after all drivers are converted,
+.remove_new() is renamed to .remove().
+
+All platform drivers below drivers/ufs/ unconditionally return zero in
+their remove callback and so can be converted trivially to the variant
+returning void.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Link: https://lore.kernel.org/r/20230917145722.1131557-1-u.kleine-koenig@pengutronix.de
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 64506b3d23a3 ("scsi: ufs: qcom: Only free platform MSIs when ESI is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/host/cdns-pltfrm.c | 5 ++---
+ drivers/ufs/host/tc-dwc-g210-pltfrm.c | 6 ++----
+ drivers/ufs/host/ti-j721e-ufs.c | 6 ++----
+ drivers/ufs/host/ufs-exynos.c | 6 ++----
+ drivers/ufs/host/ufs-hisi.c | 5 ++---
+ drivers/ufs/host/ufs-mediatek.c | 5 ++---
+ drivers/ufs/host/ufs-qcom.c | 5 ++---
+ drivers/ufs/host/ufs-renesas.c | 6 ++----
+ drivers/ufs/host/ufs-sprd.c | 5 ++---
+ 9 files changed, 18 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c
+index 56014ef302b49..66811d8d1929c 100644
+--- a/drivers/ufs/host/cdns-pltfrm.c
++++ b/drivers/ufs/host/cdns-pltfrm.c
+@@ -305,12 +305,11 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
+ *
+ * Return: 0 (success).
+ */
+-static int cdns_ufs_pltfrm_remove(struct platform_device *pdev)
++static void cdns_ufs_pltfrm_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ ufshcd_remove(hba);
+- return 0;
+ }
+
+ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
+@@ -322,7 +321,7 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
+
+ static struct platform_driver cdns_ufs_pltfrm_driver = {
+ .probe = cdns_ufs_pltfrm_probe,
+- .remove = cdns_ufs_pltfrm_remove,
++ .remove_new = cdns_ufs_pltfrm_remove,
+ .driver = {
+ .name = "cdns-ufshcd",
+ .pm = &cdns_ufs_dev_pm_ops,
+diff --git a/drivers/ufs/host/tc-dwc-g210-pltfrm.c b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+index 4d5389dd95857..a3877592604d5 100644
+--- a/drivers/ufs/host/tc-dwc-g210-pltfrm.c
++++ b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+@@ -74,14 +74,12 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
+ * @pdev: pointer to platform device structure
+ *
+ */
+-static int tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
++static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+-
+- return 0;
+ }
+
+ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
+@@ -91,7 +89,7 @@ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
+
+ static struct platform_driver tc_dwc_g210_pltfm_driver = {
+ .probe = tc_dwc_g210_pltfm_probe,
+- .remove = tc_dwc_g210_pltfm_remove,
++ .remove_new = tc_dwc_g210_pltfm_remove,
+ .driver = {
+ .name = "tc-dwc-g210-pltfm",
+ .pm = &tc_dwc_g210_pltfm_pm_ops,
+diff --git a/drivers/ufs/host/ti-j721e-ufs.c b/drivers/ufs/host/ti-j721e-ufs.c
+index 117eb7da92acd..250c22df000d5 100644
+--- a/drivers/ufs/host/ti-j721e-ufs.c
++++ b/drivers/ufs/host/ti-j721e-ufs.c
+@@ -65,13 +65,11 @@ static int ti_j721e_ufs_probe(struct platform_device *pdev)
+ return ret;
+ }
+
+-static int ti_j721e_ufs_remove(struct platform_device *pdev)
++static void ti_j721e_ufs_remove(struct platform_device *pdev)
+ {
+ of_platform_depopulate(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+-
+- return 0;
+ }
+
+ static const struct of_device_id ti_j721e_ufs_of_match[] = {
+@@ -85,7 +83,7 @@ MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match);
+
+ static struct platform_driver ti_j721e_ufs_driver = {
+ .probe = ti_j721e_ufs_probe,
+- .remove = ti_j721e_ufs_remove,
++ .remove_new = ti_j721e_ufs_remove,
+ .driver = {
+ .name = "ti-j721e-ufs",
+ .of_match_table = ti_j721e_ufs_of_match,
+diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
+index 268189f01e15b..25bc11811b4c1 100644
+--- a/drivers/ufs/host/ufs-exynos.c
++++ b/drivers/ufs/host/ufs-exynos.c
+@@ -1605,7 +1605,7 @@ static int exynos_ufs_probe(struct platform_device *pdev)
+ return err;
+ }
+
+-static int exynos_ufs_remove(struct platform_device *pdev)
++static void exynos_ufs_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+ struct exynos_ufs *ufs = ufshcd_get_variant(hba);
+@@ -1615,8 +1615,6 @@ static int exynos_ufs_remove(struct platform_device *pdev)
+
+ phy_power_off(ufs->phy);
+ phy_exit(ufs->phy);
+-
+- return 0;
+ }
+
+ static struct exynos_ufs_uic_attr exynos7_uic_attr = {
+@@ -1756,7 +1754,7 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = {
+
+ static struct platform_driver exynos_ufs_pltform = {
+ .probe = exynos_ufs_probe,
+- .remove = exynos_ufs_remove,
++ .remove_new = exynos_ufs_remove,
+ .driver = {
+ .name = "exynos-ufshc",
+ .pm = &exynos_ufs_pm_ops,
+diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
+index 5b3060cd0ab8b..0229ac0a8dbed 100644
+--- a/drivers/ufs/host/ufs-hisi.c
++++ b/drivers/ufs/host/ufs-hisi.c
+@@ -575,12 +575,11 @@ static int ufs_hisi_probe(struct platform_device *pdev)
+ return ufshcd_pltfrm_init(pdev, of_id->data);
+ }
+
+-static int ufs_hisi_remove(struct platform_device *pdev)
++static void ufs_hisi_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ ufshcd_remove(hba);
+- return 0;
+ }
+
+ static const struct dev_pm_ops ufs_hisi_pm_ops = {
+@@ -592,7 +591,7 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = {
+
+ static struct platform_driver ufs_hisi_pltform = {
+ .probe = ufs_hisi_probe,
+- .remove = ufs_hisi_remove,
++ .remove_new = ufs_hisi_remove,
+ .driver = {
+ .name = "ufshcd-hisi",
+ .pm = &ufs_hisi_pm_ops,
+diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
+index 2383ecd88f1cb..1238faec3cc68 100644
+--- a/drivers/ufs/host/ufs-mediatek.c
++++ b/drivers/ufs/host/ufs-mediatek.c
+@@ -1748,13 +1748,12 @@ static int ufs_mtk_probe(struct platform_device *pdev)
+ *
+ * Always return 0
+ */
+-static int ufs_mtk_remove(struct platform_device *pdev)
++static void ufs_mtk_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+- return 0;
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+@@ -1818,7 +1817,7 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = {
+
+ static struct platform_driver ufs_mtk_pltform = {
+ .probe = ufs_mtk_probe,
+- .remove = ufs_mtk_remove,
++ .remove_new = ufs_mtk_remove,
+ .driver = {
+ .name = "ufshcd-mtk",
+ .pm = &ufs_mtk_pm_ops,
+diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
+index 643157a92c62a..0a914fd44494d 100644
+--- a/drivers/ufs/host/ufs-qcom.c
++++ b/drivers/ufs/host/ufs-qcom.c
+@@ -1920,14 +1920,13 @@ static int ufs_qcom_probe(struct platform_device *pdev)
+ *
+ * Always returns 0
+ */
+-static int ufs_qcom_remove(struct platform_device *pdev)
++static void ufs_qcom_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+ platform_msi_domain_free_irqs(hba->dev);
+- return 0;
+ }
+
+ static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
+@@ -1959,7 +1958,7 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = {
+
+ static struct platform_driver ufs_qcom_pltform = {
+ .probe = ufs_qcom_probe,
+- .remove = ufs_qcom_remove,
++ .remove_new = ufs_qcom_remove,
+ .driver = {
+ .name = "ufshcd-qcom",
+ .pm = &ufs_qcom_pm_ops,
+diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c
+index ea3da773b1c14..3ff97112e1f6d 100644
+--- a/drivers/ufs/host/ufs-renesas.c
++++ b/drivers/ufs/host/ufs-renesas.c
+@@ -395,18 +395,16 @@ static int ufs_renesas_probe(struct platform_device *pdev)
+ return ufshcd_pltfrm_init(pdev, &ufs_renesas_vops);
+ }
+
+-static int ufs_renesas_remove(struct platform_device *pdev)
++static void ufs_renesas_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ ufshcd_remove(hba);
+-
+- return 0;
+ }
+
+ static struct platform_driver ufs_renesas_platform = {
+ .probe = ufs_renesas_probe,
+- .remove = ufs_renesas_remove,
++ .remove_new = ufs_renesas_remove,
+ .driver = {
+ .name = "ufshcd-renesas",
+ .of_match_table = of_match_ptr(ufs_renesas_of_match),
+diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c
+index 2bad75dd6d589..d8b165908809d 100644
+--- a/drivers/ufs/host/ufs-sprd.c
++++ b/drivers/ufs/host/ufs-sprd.c
+@@ -425,13 +425,12 @@ static int ufs_sprd_probe(struct platform_device *pdev)
+ return err;
+ }
+
+-static int ufs_sprd_remove(struct platform_device *pdev)
++static void ufs_sprd_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+- return 0;
+ }
+
+ static const struct dev_pm_ops ufs_sprd_pm_ops = {
+@@ -443,7 +442,7 @@ static const struct dev_pm_ops ufs_sprd_pm_ops = {
+
+ static struct platform_driver ufs_sprd_pltform = {
+ .probe = ufs_sprd_probe,
+- .remove = ufs_sprd_remove,
++ .remove_new = ufs_sprd_remove,
+ .driver = {
+ .name = "ufshcd-sprd",
+ .pm = &ufs_sprd_pm_ops,
+--
+2.43.0
+
--- /dev/null
+From 2574a5ed78f1f65bc32917a55e63a7f3c601b6ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Nov 2024 23:18:34 +0530
+Subject: scsi: ufs: pltfrm: Dellocate HBA during ufshcd_pltfrm_remove()
+
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+[ Upstream commit 897df60c16d54ad515a3d0887edab5c63da06d1f ]
+
+This will ensure that the scsi host is cleaned up properly using
+scsi_host_dev_release(). Otherwise, it may lead to memory leaks.
+
+Cc: stable@vger.kernel.org # 4.4
+Fixes: 03b1781aa978 ("[SCSI] ufs: Add Platform glue driver for ufshcd")
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20241111-ufs_bug_fix-v1-5-45ad8b62f02e@linaro.org
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/host/ufshcd-pltfrm.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
+index 05836cb8b885b..e99d89d00606b 100644
+--- a/drivers/ufs/host/ufshcd-pltfrm.c
++++ b/drivers/ufs/host/ufshcd-pltfrm.c
+@@ -402,6 +402,7 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
+
+ pm_runtime_get_sync(&pdev->dev);
+ ufshcd_remove(hba);
++ ufshcd_dealloc_host(hba);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ }
+--
+2.43.0
+
--- /dev/null
+From f2020ee0d7f8af71ab3b510729abbc7912be6399 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Nov 2024 23:18:32 +0530
+Subject: scsi: ufs: pltfrm: Disable runtime PM during removal of glue drivers
+
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+[ Upstream commit d3326e6a3f9bf1e075be2201fb704c2fdf19e2b7 ]
+
+When the UFSHCD platform glue drivers are removed, runtime PM should be
+disabled using pm_runtime_disable() to balance the enablement done in
+ufshcd_pltfrm_init(). This is also reported by PM core when the glue driver
+is removed and inserted again:
+
+ufshcd-qcom 1d84000.ufshc: Unbalanced pm_runtime_enable!
+
+So disable runtime PM using a new helper API ufshcd_pltfrm_remove(), that
+also takes care of removing ufshcd. This helper should be called during the
+remove() stage of glue drivers.
+
+Cc: stable@vger.kernel.org # 3.12
+Fixes: 62694735ca95 ("[SCSI] ufs: Add runtime PM support for UFS host controller driver")
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20241111-ufs_bug_fix-v1-3-45ad8b62f02e@linaro.org
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 1745dcdb7227 ("scsi: ufs: pltfrm: Drop PM runtime reference count after ufshcd_remove()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/host/cdns-pltfrm.c | 4 +---
+ drivers/ufs/host/tc-dwc-g210-pltfrm.c | 4 +---
+ drivers/ufs/host/ufs-exynos.c | 2 +-
+ drivers/ufs/host/ufs-hisi.c | 4 +---
+ drivers/ufs/host/ufs-mediatek.c | 4 +---
+ drivers/ufs/host/ufs-qcom.c | 2 +-
+ drivers/ufs/host/ufs-renesas.c | 4 +---
+ drivers/ufs/host/ufs-sprd.c | 4 +---
+ drivers/ufs/host/ufshcd-pltfrm.c | 13 +++++++++++++
+ drivers/ufs/host/ufshcd-pltfrm.h | 1 +
+ 10 files changed, 22 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c
+index 66811d8d1929c..b31aa84111511 100644
+--- a/drivers/ufs/host/cdns-pltfrm.c
++++ b/drivers/ufs/host/cdns-pltfrm.c
+@@ -307,9 +307,7 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
+ */
+ static void cdns_ufs_pltfrm_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
+diff --git a/drivers/ufs/host/tc-dwc-g210-pltfrm.c b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+index a3877592604d5..113e0ef7b2cf8 100644
+--- a/drivers/ufs/host/tc-dwc-g210-pltfrm.c
++++ b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+@@ -76,10 +76,8 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
+ */
+ static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+ pm_runtime_get_sync(&(pdev)->dev);
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
+diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
+index 25bc11811b4c1..1cca797a00ba0 100644
+--- a/drivers/ufs/host/ufs-exynos.c
++++ b/drivers/ufs/host/ufs-exynos.c
+@@ -1611,7 +1611,7 @@ static void exynos_ufs_remove(struct platform_device *pdev)
+ struct exynos_ufs *ufs = ufshcd_get_variant(hba);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+
+ phy_power_off(ufs->phy);
+ phy_exit(ufs->phy);
+diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
+index 0229ac0a8dbed..ceae0dd1617ed 100644
+--- a/drivers/ufs/host/ufs-hisi.c
++++ b/drivers/ufs/host/ufs-hisi.c
+@@ -577,9 +577,7 @@ static int ufs_hisi_probe(struct platform_device *pdev)
+
+ static void ufs_hisi_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ static const struct dev_pm_ops ufs_hisi_pm_ops = {
+diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
+index 1238faec3cc68..49f63b13a040d 100644
+--- a/drivers/ufs/host/ufs-mediatek.c
++++ b/drivers/ufs/host/ufs-mediatek.c
+@@ -1750,10 +1750,8 @@ static int ufs_mtk_probe(struct platform_device *pdev)
+ */
+ static void ufs_mtk_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+ pm_runtime_get_sync(&(pdev)->dev);
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ #ifdef CONFIG_PM_SLEEP
+diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
+index 1e75368930d99..762b3aa19f31d 100644
+--- a/drivers/ufs/host/ufs-qcom.c
++++ b/drivers/ufs/host/ufs-qcom.c
+@@ -1926,7 +1926,7 @@ static void ufs_qcom_remove(struct platform_device *pdev)
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ if (host->esi_enabled)
+ platform_device_msi_free_irqs_all(hba->dev);
+ }
+diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c
+index 3ff97112e1f6d..21a64b34397d8 100644
+--- a/drivers/ufs/host/ufs-renesas.c
++++ b/drivers/ufs/host/ufs-renesas.c
+@@ -397,9 +397,7 @@ static int ufs_renesas_probe(struct platform_device *pdev)
+
+ static void ufs_renesas_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ static struct platform_driver ufs_renesas_platform = {
+diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c
+index d8b165908809d..e455890cf7d49 100644
+--- a/drivers/ufs/host/ufs-sprd.c
++++ b/drivers/ufs/host/ufs-sprd.c
+@@ -427,10 +427,8 @@ static int ufs_sprd_probe(struct platform_device *pdev)
+
+ static void ufs_sprd_remove(struct platform_device *pdev)
+ {
+- struct ufs_hba *hba = platform_get_drvdata(pdev);
+-
+ pm_runtime_get_sync(&(pdev)->dev);
+- ufshcd_remove(hba);
++ ufshcd_pltfrm_remove(pdev);
+ }
+
+ static const struct dev_pm_ops ufs_sprd_pm_ops = {
+diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
+index 797a4dfe45d90..0dc8651eabc22 100644
+--- a/drivers/ufs/host/ufshcd-pltfrm.c
++++ b/drivers/ufs/host/ufshcd-pltfrm.c
+@@ -392,6 +392,19 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
+ }
+ EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
+
++/**
++ * ufshcd_pltfrm_remove - Remove ufshcd platform
++ * @pdev: pointer to Platform device handle
++ */
++void ufshcd_pltfrm_remove(struct platform_device *pdev)
++{
++ struct ufs_hba *hba = platform_get_drvdata(pdev);
++
++ ufshcd_remove(hba);
++ pm_runtime_disable(&pdev->dev);
++}
++EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
++
+ MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
+ MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
+ MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
+diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
+index 2df108f4ac131..1cfc4f8ea07eb 100644
+--- a/drivers/ufs/host/ufshcd-pltfrm.h
++++ b/drivers/ufs/host/ufshcd-pltfrm.h
+@@ -31,6 +31,7 @@ int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *dev_param,
+ void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param);
+ int ufshcd_pltfrm_init(struct platform_device *pdev,
+ const struct ufs_hba_variant_ops *vops);
++void ufshcd_pltfrm_remove(struct platform_device *pdev);
+ int ufshcd_populate_vreg(struct device *dev, const char *name,
+ struct ufs_vreg **out_vreg);
+
+--
+2.43.0
+
--- /dev/null
+From ea4799f2d7ae0a481aa95411f6f649d09f9f62f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Nov 2024 23:18:33 +0530
+Subject: scsi: ufs: pltfrm: Drop PM runtime reference count after
+ ufshcd_remove()
+
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+[ Upstream commit 1745dcdb7227102e16248a324c600b9121c8f6df ]
+
+During the remove stage of glue drivers, some of them are incrementing the
+reference count using pm_runtime_get_sync(), before removing the ufshcd
+using ufshcd_remove(). But they are not dropping that reference count after
+ufshcd_remove() to balance the refcount.
+
+So drop the reference count by calling pm_runtime_put_noidle() after
+ufshcd_remove(). Since the behavior is applicable to all glue drivers, move
+the PM handling to ufshcd_pltfrm_remove().
+
+Cc: stable@vger.kernel.org # 3.12
+Fixes: 62694735ca95 ("[SCSI] ufs: Add runtime PM support for UFS host controller driver")
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20241111-ufs_bug_fix-v1-4-45ad8b62f02e@linaro.org
+Reviewed-by: Peter Wang <peter.wang@mediatek.com>
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/host/tc-dwc-g210-pltfrm.c | 1 -
+ drivers/ufs/host/ufs-exynos.c | 1 -
+ drivers/ufs/host/ufs-mediatek.c | 1 -
+ drivers/ufs/host/ufs-qcom.c | 1 -
+ drivers/ufs/host/ufs-sprd.c | 1 -
+ drivers/ufs/host/ufshcd-pltfrm.c | 2 ++
+ 6 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/ufs/host/tc-dwc-g210-pltfrm.c b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+index 113e0ef7b2cf8..c6f8565ede21a 100644
+--- a/drivers/ufs/host/tc-dwc-g210-pltfrm.c
++++ b/drivers/ufs/host/tc-dwc-g210-pltfrm.c
+@@ -76,7 +76,6 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
+ */
+ static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
+ {
+- pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_pltfrm_remove(pdev);
+ }
+
+diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
+index 1cca797a00ba0..4418c497a6d71 100644
+--- a/drivers/ufs/host/ufs-exynos.c
++++ b/drivers/ufs/host/ufs-exynos.c
+@@ -1610,7 +1610,6 @@ static void exynos_ufs_remove(struct platform_device *pdev)
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+ struct exynos_ufs *ufs = ufshcd_get_variant(hba);
+
+- pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_pltfrm_remove(pdev);
+
+ phy_power_off(ufs->phy);
+diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
+index 49f63b13a040d..64d85e63b7501 100644
+--- a/drivers/ufs/host/ufs-mediatek.c
++++ b/drivers/ufs/host/ufs-mediatek.c
+@@ -1750,7 +1750,6 @@ static int ufs_mtk_probe(struct platform_device *pdev)
+ */
+ static void ufs_mtk_remove(struct platform_device *pdev)
+ {
+- pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_pltfrm_remove(pdev);
+ }
+
+diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
+index 762b3aa19f31d..0dc4c14e9f35c 100644
+--- a/drivers/ufs/host/ufs-qcom.c
++++ b/drivers/ufs/host/ufs-qcom.c
+@@ -1925,7 +1925,6 @@ static void ufs_qcom_remove(struct platform_device *pdev)
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+
+- pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_pltfrm_remove(pdev);
+ if (host->esi_enabled)
+ platform_device_msi_free_irqs_all(hba->dev);
+diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c
+index e455890cf7d49..d220978c2d8c8 100644
+--- a/drivers/ufs/host/ufs-sprd.c
++++ b/drivers/ufs/host/ufs-sprd.c
+@@ -427,7 +427,6 @@ static int ufs_sprd_probe(struct platform_device *pdev)
+
+ static void ufs_sprd_remove(struct platform_device *pdev)
+ {
+- pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_pltfrm_remove(pdev);
+ }
+
+diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
+index 0dc8651eabc22..05836cb8b885b 100644
+--- a/drivers/ufs/host/ufshcd-pltfrm.c
++++ b/drivers/ufs/host/ufshcd-pltfrm.c
+@@ -400,8 +400,10 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
++ pm_runtime_get_sync(&pdev->dev);
+ ufshcd_remove(hba);
+ pm_runtime_disable(&pdev->dev);
++ pm_runtime_put_noidle(&pdev->dev);
+ }
+ EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
+
+--
+2.43.0
+
--- /dev/null
+From fa58fa5bb0bd735892f87767755f409f2e7dd7f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Nov 2024 23:18:31 +0530
+Subject: scsi: ufs: qcom: Only free platform MSIs when ESI is enabled
+
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+[ Upstream commit 64506b3d23a337e98a74b18dcb10c8619365f2bd ]
+
+Otherwise, it will result in a NULL pointer dereference as below:
+
+Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008
+Call trace:
+ mutex_lock+0xc/0x54
+ platform_device_msi_free_irqs_all+0x14/0x20
+ ufs_qcom_remove+0x34/0x48 [ufs_qcom]
+ platform_remove+0x28/0x44
+ device_remove+0x4c/0x80
+ device_release_driver_internal+0xd8/0x178
+ driver_detach+0x50/0x9c
+ bus_remove_driver+0x6c/0xbc
+ driver_unregister+0x30/0x60
+ platform_driver_unregister+0x14/0x20
+ ufs_qcom_pltform_exit+0x18/0xb94 [ufs_qcom]
+ __arm64_sys_delete_module+0x180/0x260
+ invoke_syscall+0x44/0x100
+ el0_svc_common.constprop.0+0xc0/0xe0
+ do_el0_svc+0x1c/0x28
+ el0_svc+0x34/0xdc
+ el0t_64_sync_handler+0xc0/0xc4
+ el0t_64_sync+0x190/0x194
+
+Cc: stable@vger.kernel.org # 6.3
+Fixes: 519b6274a777 ("scsi: ufs: qcom: Add MCQ ESI config vendor specific ops")
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20241111-ufs_bug_fix-v1-2-45ad8b62f02e@linaro.org
+Reviewed-by: Bean Huo <beanhuo@micron.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ufs/host/ufs-qcom.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
+index d77cfb2ab1acd..1e75368930d99 100644
+--- a/drivers/ufs/host/ufs-qcom.c
++++ b/drivers/ufs/host/ufs-qcom.c
+@@ -1923,10 +1923,12 @@ static int ufs_qcom_probe(struct platform_device *pdev)
+ static void ufs_qcom_remove(struct platform_device *pdev)
+ {
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
++ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+- platform_device_msi_free_irqs_all(hba->dev);
++ if (host->esi_enabled)
++ platform_device_msi_free_irqs_all(hba->dev);
+ }
+
+ static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
+--
+2.43.0
+
smb-client-don-t-try-following-dfs-links-in-cifs_tre.patch
setlocalversion-work-around-git-describe-performance.patch
io_uring-tctx-work-around-xa_store-allocation-error-.patch
+scsi-ufs-convert-all-platform-drivers-to-return-void.patch
+genirq-irqdomain-add-domain_bus_device_msi.patch
+platform-msi-prepare-for-real-per-device-domains.patch
+irqchip-convert-all-platform-msi-users-to-the-new-ap.patch
+scsi-ufs-qcom-only-free-platform-msis-when-esi-is-en.patch
+scsi-ufs-pltfrm-disable-runtime-pm-during-removal-of.patch
+scsi-ufs-pltfrm-drop-pm-runtime-reference-count-afte.patch
+scsi-ufs-pltfrm-dellocate-hba-during-ufshcd_pltfrm_r.patch
+sched-numa-fix-mm-numa_scan_seq-based-unconditional-.patch
+sched-numa-fix-memory-leak-due-to-the-overwritten-vm.patch
+mempolicy-fix-migrate_pages-2-syscall-return-nr_fail.patch
+mm-mempolicy-fix-migrate_to_node-assuming-there-is-a.patch