]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.16-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 Sep 2025 11:09:16 +0000 (13:09 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 Sep 2025 11:09:16 +0000 (13:09 +0200)
added patches:
iommufd-fix-race-during-abort-for-file-descriptors.patch
spi-cadence-qspi-defer-runtime-support-on-socfpga-if-reset-bit-is-enabled.patch
spi-cadence-quadspi-implement-refcount-to-handle-unbind-during-busy.patch

queue-6.16/iommufd-fix-race-during-abort-for-file-descriptors.patch [new file with mode: 0644]
queue-6.16/series
queue-6.16/spi-cadence-qspi-defer-runtime-support-on-socfpga-if-reset-bit-is-enabled.patch [new file with mode: 0644]
queue-6.16/spi-cadence-quadspi-implement-refcount-to-handle-unbind-during-busy.patch [new file with mode: 0644]

diff --git a/queue-6.16/iommufd-fix-race-during-abort-for-file-descriptors.patch b/queue-6.16/iommufd-fix-race-during-abort-for-file-descriptors.patch
new file mode 100644 (file)
index 0000000..aafeb88
--- /dev/null
@@ -0,0 +1,203 @@
+From stable+bounces-181947-greg=kroah.com@vger.kernel.org Mon Sep 29 17:47:52 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Sep 2025 11:47:41 -0400
+Subject: iommufd: Fix race during abort for file descriptors
+To: stable@vger.kernel.org
+Cc: Jason Gunthorpe <jgg@nvidia.com>, Nicolin Chen <nicolinc@nvidia.com>, Nirmoy Das <nirmoyd@nvidia.com>, Kevin Tian <kevin.tian@intel.com>, syzbot+80620e2d0d0a33b09f93@syzkaller.appspotmail.com, Sasha Levin <sashal@kernel.org>
+Message-ID: <20250929154741.136377-1-sashal@kernel.org>
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ Upstream commit 4e034bf045b12852a24d5d33f2451850818ba0c1 ]
+
+fput() doesn't actually call file_operations release() synchronously, it
+puts the file on a work queue and it will be released eventually.
+
+This is normally fine, except for iommufd the file and the iommufd_object
+are tied to gether. The file has the object as it's private_data and holds
+a users refcount, while the object is expected to remain alive as long as
+the file is.
+
+When the allocation of a new object aborts before installing the file it
+will fput() the file and then go on to immediately kfree() the obj. This
+causes a UAF once the workqueue completes the fput() and tries to
+decrement the users refcount.
+
+Fix this by putting the core code in charge of the file lifetime, and call
+__fput_sync() during abort to ensure that release() is called before
+kfree. __fput_sync() is a bit too tricky to open code in all the object
+implementations. Instead the objects tell the core code where the file
+pointer is and the core will take care of the life cycle.
+
+If the object is successfully allocated then the file will hold a users
+refcount and the iommufd_object cannot be destroyed.
+
+It is worth noting that close(); ioctl(IOMMU_DESTROY); doesn't have an
+issue because close() is already using a synchronous version of fput().
+
+The UAF looks like this:
+
+    BUG: KASAN: slab-use-after-free in iommufd_eventq_fops_release+0x45/0xc0 drivers/iommu/iommufd/eventq.c:376
+    Write of size 4 at addr ffff888059c97804 by task syz.0.46/6164
+
+    CPU: 0 UID: 0 PID: 6164 Comm: syz.0.46 Not tainted syzkaller #0 PREEMPT(full)
+    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025
+    Call Trace:
+     <TASK>
+     __dump_stack lib/dump_stack.c:94 [inline]
+     dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
+     print_address_description mm/kasan/report.c:378 [inline]
+     print_report+0xcd/0x630 mm/kasan/report.c:482
+     kasan_report+0xe0/0x110 mm/kasan/report.c:595
+     check_region_inline mm/kasan/generic.c:183 [inline]
+     kasan_check_range+0x100/0x1b0 mm/kasan/generic.c:189
+     instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
+     atomic_fetch_sub_release include/linux/atomic/atomic-instrumented.h:400 [inline]
+     __refcount_dec include/linux/refcount.h:455 [inline]
+     refcount_dec include/linux/refcount.h:476 [inline]
+     iommufd_eventq_fops_release+0x45/0xc0 drivers/iommu/iommufd/eventq.c:376
+     __fput+0x402/0xb70 fs/file_table.c:468
+     task_work_run+0x14d/0x240 kernel/task_work.c:227
+     resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
+     exit_to_user_mode_loop+0xeb/0x110 kernel/entry/common.c:43
+     exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline]
+     syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline]
+     syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline]
+     do_syscall_64+0x41c/0x4c0 arch/x86/entry/syscall_64.c:100
+     entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Link: https://patch.msgid.link/r/1-v1-02cd136829df+31-iommufd_syz_fput_jgg@nvidia.com
+Cc: stable@vger.kernel.org
+Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object")
+Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
+Reviewed-by: Nirmoy Das <nirmoyd@nvidia.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Tested-by: Nicolin Chen <nicolinc@nvidia.com>
+Reported-by: syzbot+80620e2d0d0a33b09f93@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/r/68c8583d.050a0220.2ff435.03a2.GAE@google.com
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+[ Adjust context ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/iommufd/eventq.c |    9 ++-------
+ drivers/iommu/iommufd/main.c   |   35 ++++++++++++++++++++++++++++++++---
+ 2 files changed, 34 insertions(+), 10 deletions(-)
+
+--- a/drivers/iommu/iommufd/eventq.c
++++ b/drivers/iommu/iommufd/eventq.c
+@@ -393,12 +393,12 @@ static int iommufd_eventq_init(struct io
+                              const struct file_operations *fops)
+ {
+       struct file *filep;
+-      int fdno;
+       spin_lock_init(&eventq->lock);
+       INIT_LIST_HEAD(&eventq->deliver);
+       init_waitqueue_head(&eventq->wait_queue);
++      /* The filep is fput() by the core code during failure */
+       filep = anon_inode_getfile(name, fops, eventq, O_RDWR);
+       if (IS_ERR(filep))
+               return PTR_ERR(filep);
+@@ -408,10 +408,7 @@ static int iommufd_eventq_init(struct io
+       eventq->filep = filep;
+       refcount_inc(&eventq->obj.users);
+-      fdno = get_unused_fd_flags(O_CLOEXEC);
+-      if (fdno < 0)
+-              fput(filep);
+-      return fdno;
++      return get_unused_fd_flags(O_CLOEXEC);
+ }
+ static const struct file_operations iommufd_fault_fops =
+@@ -455,7 +452,6 @@ int iommufd_fault_alloc(struct iommufd_u
+       return 0;
+ out_put_fdno:
+       put_unused_fd(fdno);
+-      fput(fault->common.filep);
+ out_abort:
+       iommufd_object_abort_and_destroy(ucmd->ictx, &fault->common.obj);
+@@ -542,7 +538,6 @@ int iommufd_veventq_alloc(struct iommufd
+ out_put_fdno:
+       put_unused_fd(fdno);
+-      fput(veventq->common.filep);
+ out_abort:
+       iommufd_object_abort_and_destroy(ucmd->ictx, &veventq->common.obj);
+ out_unlock_veventqs:
+--- a/drivers/iommu/iommufd/main.c
++++ b/drivers/iommu/iommufd/main.c
+@@ -23,6 +23,7 @@
+ #include "iommufd_test.h"
+ struct iommufd_object_ops {
++      size_t file_offset;
+       void (*destroy)(struct iommufd_object *obj);
+       void (*abort)(struct iommufd_object *obj);
+ };
+@@ -71,10 +72,30 @@ void iommufd_object_abort(struct iommufd
+ void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx,
+                                     struct iommufd_object *obj)
+ {
+-      if (iommufd_object_ops[obj->type].abort)
+-              iommufd_object_ops[obj->type].abort(obj);
++      const struct iommufd_object_ops *ops = &iommufd_object_ops[obj->type];
++
++      if (ops->file_offset) {
++              struct file **filep = ((void *)obj) + ops->file_offset;
++
++              /*
++               * A file should hold a users refcount while the file is open
++               * and put it back in its release. The file should hold a
++               * pointer to obj in their private data. Normal fput() is
++               * deferred to a workqueue and can get out of order with the
++               * following kfree(obj). Using the sync version ensures the
++               * release happens immediately. During abort we require the file
++               * refcount is one at this point - meaning the object alloc
++               * function cannot do anything to allow another thread to take a
++               * refcount prior to a guaranteed success.
++               */
++              if (*filep)
++                      __fput_sync(*filep);
++      }
++
++      if (ops->abort)
++              ops->abort(obj);
+       else
+-              iommufd_object_ops[obj->type].destroy(obj);
++              ops->destroy(obj);
+       iommufd_object_abort(ictx, obj);
+ }
+@@ -493,6 +514,12 @@ void iommufd_ctx_put(struct iommufd_ctx
+ }
+ EXPORT_SYMBOL_NS_GPL(iommufd_ctx_put, "IOMMUFD");
++#define IOMMUFD_FILE_OFFSET(_struct, _filep, _obj)                           \
++      .file_offset = (offsetof(_struct, _filep) +                          \
++                      BUILD_BUG_ON_ZERO(!__same_type(                      \
++                              struct file *, ((_struct *)NULL)->_filep)) + \
++                      BUILD_BUG_ON_ZERO(offsetof(_struct, _obj)))
++
+ static const struct iommufd_object_ops iommufd_object_ops[] = {
+       [IOMMUFD_OBJ_ACCESS] = {
+               .destroy = iommufd_access_destroy_object,
+@@ -502,6 +529,7 @@ static const struct iommufd_object_ops i
+       },
+       [IOMMUFD_OBJ_FAULT] = {
+               .destroy = iommufd_fault_destroy,
++              IOMMUFD_FILE_OFFSET(struct iommufd_fault, common.filep, common.obj),
+       },
+       [IOMMUFD_OBJ_HWPT_PAGING] = {
+               .destroy = iommufd_hwpt_paging_destroy,
+@@ -520,6 +548,7 @@ static const struct iommufd_object_ops i
+       [IOMMUFD_OBJ_VEVENTQ] = {
+               .destroy = iommufd_veventq_destroy,
+               .abort = iommufd_veventq_abort,
++              IOMMUFD_FILE_OFFSET(struct iommufd_veventq, common.filep, common.obj),
+       },
+       [IOMMUFD_OBJ_VIOMMU] = {
+               .destroy = iommufd_viommu_destroy,
index 97839511c02e06ef19e3816225a6fbcc460a8dcb..f62c7a6445cea03eb49d2939058b301ab60c4b8b 100644 (file)
@@ -138,3 +138,6 @@ wifi-iwlwifi-fix-byte-count-table-for-old-devices.patch
 wifi-iwlwifi-pcie-fix-byte-count-table-for-some-devices.patch
 sched_ext-idle-make-local-functions-static-in-ext_idle.c.patch
 sched_ext-idle-handle-migration-disabled-tasks-in-bpf-code.patch
+spi-cadence-quadspi-implement-refcount-to-handle-unbind-during-busy.patch
+spi-cadence-qspi-defer-runtime-support-on-socfpga-if-reset-bit-is-enabled.patch
+iommufd-fix-race-during-abort-for-file-descriptors.patch
diff --git a/queue-6.16/spi-cadence-qspi-defer-runtime-support-on-socfpga-if-reset-bit-is-enabled.patch b/queue-6.16/spi-cadence-qspi-defer-runtime-support-on-socfpga-if-reset-bit-is-enabled.patch
new file mode 100644 (file)
index 0000000..e3735b9
--- /dev/null
@@ -0,0 +1,160 @@
+From stable+bounces-181994-greg=kroah.com@vger.kernel.org Mon Sep 29 21:43:08 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Sep 2025 15:42:55 -0400
+Subject: spi: cadence-qspi: defer runtime support on socfpga if reset bit is enabled
+To: stable@vger.kernel.org
+Cc: Khairul Anuar Romli <khairul.anuar.romli@altera.com>, Adrian Ng Ho Yin <adrianhoyin.ng@altera.com>, Niravkumar L Rabara <nirav.rabara@altera.com>, Matthew Gerlach <matthew.gerlach@altera.com>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20250929194255.332788-2-sashal@kernel.org>
+
+From: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+
+[ Upstream commit 30dbc1c8d50f13c1581b49abe46fe89f393eacbf ]
+
+Enabling runtime PM allows the kernel to gate clocks and power to idle
+devices. On SoCFPGA, a warm reset does not fully reinitialize these
+domains.This leaves devices suspended and powered down, preventing U-Boot
+or the kernel from reusing them after a warm reset, which breaks the boot
+process.
+
+Fixes: 4892b374c9b7 ("mtd: spi-nor: cadence-quadspi: Add runtime PM support")
+CC: stable@vger.kernel.org # 6.12+
+Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com>
+Reviewed-by: Niravkumar L Rabara <nirav.rabara@altera.com>
+Reviewed-by: Matthew Gerlach <matthew.gerlach@altera.com>
+Link: https://patch.msgid.link/910aad68ba5d948919a7b90fa85a2fadb687229b.1757491372.git.khairul.anuar.romli@altera.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-cadence-quadspi.c |   57 +++++++++++++++++++++++++-------------
+ 1 file changed, 38 insertions(+), 19 deletions(-)
+
+--- a/drivers/spi/spi-cadence-quadspi.c
++++ b/drivers/spi/spi-cadence-quadspi.c
+@@ -46,6 +46,7 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SP
+ #define CQSPI_DMA_SET_MASK            BIT(7)
+ #define CQSPI_SUPPORT_DEVICE_RESET    BIT(8)
+ #define CQSPI_DISABLE_STIG_MODE               BIT(9)
++#define CQSPI_DISABLE_RUNTIME_PM      BIT(10)
+ /* Capabilities */
+ #define CQSPI_SUPPORTS_OCTAL          BIT(0)
+@@ -1468,14 +1469,17 @@ static int cqspi_exec_mem_op(struct spi_
+       int ret;
+       struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
+       struct device *dev = &cqspi->pdev->dev;
++      const struct cqspi_driver_platdata *ddata = of_device_get_match_data(dev);
+       if (refcount_read(&cqspi->inflight_ops) == 0)
+               return -ENODEV;
+-      ret = pm_runtime_resume_and_get(dev);
+-      if (ret) {
+-              dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
+-              return ret;
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
++              ret = pm_runtime_resume_and_get(dev);
++              if (ret) {
++                      dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
++                      return ret;
++              }
+       }
+       if (!refcount_read(&cqspi->refcount))
+@@ -1491,8 +1495,10 @@ static int cqspi_exec_mem_op(struct spi_
+       ret = cqspi_mem_process(mem, op);
+-      pm_runtime_mark_last_busy(dev);
+-      pm_runtime_put_autosuspend(dev);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
++              pm_runtime_mark_last_busy(dev);
++              pm_runtime_put_autosuspend(dev);
++      }
+       if (ret)
+               dev_err(&mem->spi->dev, "operation failed with %d\n", ret);
+@@ -1986,11 +1992,12 @@ static int cqspi_probe(struct platform_d
+                       goto probe_setup_failed;
+       }
+-      pm_runtime_enable(dev);
+-
+-      pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT);
+-      pm_runtime_use_autosuspend(dev);
+-      pm_runtime_get_noresume(dev);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
++              pm_runtime_enable(dev);
++              pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT);
++              pm_runtime_use_autosuspend(dev);
++              pm_runtime_get_noresume(dev);
++      }
+       ret = spi_register_controller(host);
+       if (ret) {
+@@ -1998,13 +2005,17 @@ static int cqspi_probe(struct platform_d
+               goto probe_setup_failed;
+       }
+-      pm_runtime_mark_last_busy(dev);
+-      pm_runtime_put_autosuspend(dev);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
++              pm_runtime_put_autosuspend(dev);
++              pm_runtime_mark_last_busy(dev);
++              pm_runtime_put_autosuspend(dev);
++      }
+       return 0;
+ probe_setup_failed:
+       cqspi_controller_enable(cqspi, 0);
+-      pm_runtime_disable(dev);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
++              pm_runtime_disable(dev);
+ probe_reset_failed:
+       if (cqspi->is_jh7110)
+               cqspi_jh7110_disable_clk(pdev, cqspi);
+@@ -2015,7 +2026,11 @@ probe_clk_failed:
+ static void cqspi_remove(struct platform_device *pdev)
+ {
++      const struct cqspi_driver_platdata *ddata;
+       struct cqspi_st *cqspi = platform_get_drvdata(pdev);
++      struct device *dev = &pdev->dev;
++
++      ddata = of_device_get_match_data(dev);
+       refcount_set(&cqspi->refcount, 0);
+@@ -2028,14 +2043,17 @@ static void cqspi_remove(struct platform
+       if (cqspi->rx_chan)
+               dma_release_channel(cqspi->rx_chan);
+-      if (pm_runtime_get_sync(&pdev->dev) >= 0)
+-              clk_disable(cqspi->clk);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
++              if (pm_runtime_get_sync(&pdev->dev) >= 0)
++                      clk_disable(cqspi->clk);
+       if (cqspi->is_jh7110)
+               cqspi_jh7110_disable_clk(pdev, cqspi);
+-      pm_runtime_put_sync(&pdev->dev);
+-      pm_runtime_disable(&pdev->dev);
++      if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
++              pm_runtime_put_sync(&pdev->dev);
++              pm_runtime_disable(&pdev->dev);
++      }
+ }
+ static int cqspi_runtime_suspend(struct device *dev)
+@@ -2114,7 +2132,8 @@ static const struct cqspi_driver_platdat
+       .quirks = CQSPI_DISABLE_DAC_MODE
+                       | CQSPI_NO_SUPPORT_WR_COMPLETION
+                       | CQSPI_SLOW_SRAM
+-                      | CQSPI_DISABLE_STIG_MODE,
++                      | CQSPI_DISABLE_STIG_MODE
++                      | CQSPI_DISABLE_RUNTIME_PM,
+ };
+ static const struct cqspi_driver_platdata versal_ospi = {
diff --git a/queue-6.16/spi-cadence-quadspi-implement-refcount-to-handle-unbind-during-busy.patch b/queue-6.16/spi-cadence-quadspi-implement-refcount-to-handle-unbind-during-busy.patch
new file mode 100644 (file)
index 0000000..dcae7f1
--- /dev/null
@@ -0,0 +1,124 @@
+From stable+bounces-181993-greg=kroah.com@vger.kernel.org Mon Sep 29 21:43:06 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 29 Sep 2025 15:42:54 -0400
+Subject: spi: cadence-quadspi: Implement refcount to handle unbind during busy
+To: stable@vger.kernel.org
+Cc: Khairul Anuar Romli <khairul.anuar.romli@altera.com>, Matthew Gerlach <matthew.gerlach@altera.com>, Niravkumar L Rabara <nirav.rabara@altera.com>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20250929194255.332788-1-sashal@kernel.org>
+
+From: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+
+[ Upstream commit 7446284023e8ef694fb392348185349c773eefb3 ]
+
+driver support indirect read and indirect write operation with
+assumption no force device removal(unbind) operation. However
+force device removal(removal) is still available to root superuser.
+
+Unbinding driver during operation causes kernel crash. This changes
+ensure driver able to handle such operation for indirect read and
+indirect write by implementing refcount to track attached devices
+to the controller and gracefully wait and until attached devices
+remove operation completed before proceed with removal operation.
+
+Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+Reviewed-by: Matthew Gerlach <matthew.gerlach@altera.com>
+Reviewed-by: Niravkumar L Rabara <nirav.rabara@altera.com>
+Link: https://patch.msgid.link/8704fd6bd2ff4d37bba4a0eacf5eba3ba001079e.1756168074.git.khairul.anuar.romli@altera.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 30dbc1c8d50f ("spi: cadence-qspi: defer runtime support on socfpga if reset bit is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-cadence-quadspi.c |   33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/spi/spi-cadence-quadspi.c
++++ b/drivers/spi/spi-cadence-quadspi.c
+@@ -108,6 +108,8 @@ struct cqspi_st {
+       bool                    is_jh7110; /* Flag for StarFive JH7110 SoC */
+       bool                    disable_stig_mode;
++      refcount_t              refcount;
++      refcount_t              inflight_ops;
+       const struct cqspi_driver_platdata *ddata;
+ };
+@@ -735,6 +737,9 @@ static int cqspi_indirect_read_execute(s
+       u8 *rxbuf_end = rxbuf + n_rx;
+       int ret = 0;
++      if (!refcount_read(&cqspi->refcount))
++              return -ENODEV;
++
+       writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+       writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);
+@@ -1071,6 +1076,9 @@ static int cqspi_indirect_write_execute(
+       unsigned int write_bytes;
+       int ret;
++      if (!refcount_read(&cqspi->refcount))
++              return -ENODEV;
++
+       writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
+       writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);
+@@ -1461,12 +1469,26 @@ static int cqspi_exec_mem_op(struct spi_
+       struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
+       struct device *dev = &cqspi->pdev->dev;
++      if (refcount_read(&cqspi->inflight_ops) == 0)
++              return -ENODEV;
++
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret) {
+               dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
+               return ret;
+       }
++      if (!refcount_read(&cqspi->refcount))
++              return -EBUSY;
++
++      refcount_inc(&cqspi->inflight_ops);
++
++      if (!refcount_read(&cqspi->refcount)) {
++              if (refcount_read(&cqspi->inflight_ops))
++                      refcount_dec(&cqspi->inflight_ops);
++              return -EBUSY;
++      }
++
+       ret = cqspi_mem_process(mem, op);
+       pm_runtime_mark_last_busy(dev);
+@@ -1475,6 +1497,9 @@ static int cqspi_exec_mem_op(struct spi_
+       if (ret)
+               dev_err(&mem->spi->dev, "operation failed with %d\n", ret);
++      if (refcount_read(&cqspi->inflight_ops) > 1)
++              refcount_dec(&cqspi->inflight_ops);
++
+       return ret;
+ }
+@@ -1926,6 +1951,9 @@ static int cqspi_probe(struct platform_d
+               }
+       }
++      refcount_set(&cqspi->refcount, 1);
++      refcount_set(&cqspi->inflight_ops, 1);
++
+       ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
+                              pdev->name, cqspi);
+       if (ret) {
+@@ -1989,6 +2017,11 @@ static void cqspi_remove(struct platform
+ {
+       struct cqspi_st *cqspi = platform_get_drvdata(pdev);
++      refcount_set(&cqspi->refcount, 0);
++
++      if (!refcount_dec_and_test(&cqspi->inflight_ops))
++              cqspi_wait_idle(cqspi);
++
+       spi_unregister_controller(cqspi->host);
+       cqspi_controller_enable(cqspi, 0);