From nobody Sun May 24 21:38:03 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AFCB0367B82; Thu, 21 May 2026 06:12:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779343934; cv=none; b=kkjlbLqDY/ZKQm4qvatMOxn+jKerBALGzLAlE40li6cxoGrSZxKozev1LuYXFW+65XWvjs1iVBT6cJUEv/WIyIUpgIdoSYF+kKoFf8Mdegt1Kgkv1ph6FWJnYMiMNgZNuDitm4ZtkCok1q/7j5TxqqoyKkxydlDVG7kozfGQXhU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779343934; c=relaxed/simple; bh=no8/ksF6iocgkvhCbfu2koPeZ6w4U5V6/KsTBkWB+Ss=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Xvcmn0jUieFhtUvM7ux6Ad6wx1NjST8ROgCP5l8+uiZeWXCRRkLagX4jpE9qtR8Bpc+nFZrYCijQ0DVBQQ6SwE60oFI3NBDGGbLarhiPmXswd+9XbUlBdTgruvEqK+CpeQ1fzoVQKlJQTmOgcxA8fQJWY7G/aU9tRmIWj8xIlJo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=nG7yS2Ih; arc=none smtp.client-ip=117.135.210.3 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="nG7yS2Ih" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=Mz HLUAcW99NN4f3Mos8xquCTzlyXA8yMa7BFIECulg0=; b=nG7yS2IhSL4OVKAJPF QJQupulxK3hgAMojjCpIbum84RMgGSgjDqnzzSkFk5ShEbLXxVXzxEy/mhogFTpQ 00uVax8R/xG5v/qGFqxnwu7KReZ2Ry0EFcOwh2MWf0ol9nqRLh3OibER+SMJADyE rdcTrUewxBeQfrPADtjBNfNZ4= Received: from China-163-team (unknown []) by gzga-smtp-mtada-g1-0 (Coremail) with SMTP id _____wCXlHcFog5qqHUACg--.53269S2; Thu, 21 May 2026 14:11:19 +0800 (CST) From: Wenshan Lan To: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, Fabian Godehardt , Mark Brown , Wenshan Lan Subject: [PATCH 6.6.y] spi: spidev: fix lock inversion between spi_lock and buf_lock Date: Thu, 21 May 2026 14:10:51 +0800 Message-ID: <20260521061051.31928-1-jetlan9@163.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wCXlHcFog5qqHUACg--.53269S2 X-Coremail-Antispam: 1Uf129KBjvJXoW3JF48Ar47tF45Xr4ftr1kKrg_yoWxAF4rpF W3KayxArsYyr4kXrn7Cw43uw15Xw18KFWUX347AFyfCF15urnIqFyUJFy8ZFWrJFyxurs8 AF1vqry5Ar4Yvw7anT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0pEWE_ZUUUUU= X-CM-SenderInfo: xmhwztjqz6il2tof0z/xtbC7AnIkWoOoglM8QAA3O Content-Type: text/plain; charset="utf-8" From: Fabian Godehardt [ Upstream commit 40534d19ed2afb880ecf202dab26a8e7a5808d16 ] The spidev driver previously used two mutexes, spi_lock and buf_lock, but acquired them in different orders depending on the code path: write()/read(): buf_lock -> spi_lock ioctl(): spi_lock -> buf_lock This AB-BA locking pattern triggers lockdep warnings and can cause real deadlocks: WARNING: possible circular locking dependency detected spidev_ioctl() -> mutex_lock(&spidev->buf_lock) spidev_sync_write() -> mutex_lock(&spidev->spi_lock) *** DEADLOCK *** The issue is reproducible with a simple userspace program that performs write() and SPI_IOC_WR_MAX_SPEED_HZ ioctl() calls from separate threads on the same spidev file descriptor. Fix this by simplifying the locking model and removing the lock inversion entirely. spidev_sync() no longer performs any locking, and all callers serialize access using spi_lock. buf_lock is removed since its functionality is fully covered by spi_lock, eliminating the possibility of lock ordering issues. This removes the lock inversion and prevents deadlocks without changing userspace ABI or behaviour. Signed-off-by: Fabian Godehardt Link: https://patch.msgid.link/20260211072616.489522-1-fg@emlix.com Signed-off-by: Mark Brown [ Minor context conflict resolved. ] Signed-off-by: Wenshan Lan --- Testing: - Verified on Intel Alder Lake-N (x86_64) target running 6.6.137-yocto-st= andard - Kernel built with CONFIG_PROVE_LOCKING=3Dy - Before patch: lockdep reports circular locking dependency (*** DEADLOCK= ***) - After patch: reproducer runs cleanly with no lockdep warnings --- drivers/spi/spidev.c | 63 ++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 16bb4fc3a4ba..2024532dfc44 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -74,7 +74,6 @@ struct spidev_data { struct list_head device_entry; =20 /* TX/RX buffers are NULL unless this device is open (users > 0) */ - struct mutex buf_lock; unsigned users; u8 *tx_buffer; u8 *rx_buffer; @@ -102,24 +101,6 @@ spidev_sync_unlocked(struct spi_device *spi, struct sp= i_message *message) return status; } =20 -static ssize_t -spidev_sync(struct spidev_data *spidev, struct spi_message *message) -{ - ssize_t status; - struct spi_device *spi; - - mutex_lock(&spidev->spi_lock); - spi =3D spidev->spi; - - if (spi =3D=3D NULL) - status =3D -ESHUTDOWN; - else - status =3D spidev_sync_unlocked(spi, message); - - mutex_unlock(&spidev->spi_lock); - return status; -} - static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) { @@ -132,7 +113,8 @@ spidev_sync_write(struct spidev_data *spidev, size_t le= n) =20 spi_message_init(&m); spi_message_add_tail(&t, &m); - return spidev_sync(spidev, &m); + + return spidev_sync_unlocked(spidev->spi, &m); } =20 static inline ssize_t @@ -147,7 +129,8 @@ spidev_sync_read(struct spidev_data *spidev, size_t len) =20 spi_message_init(&m); spi_message_add_tail(&t, &m); - return spidev_sync(spidev, &m); + + return spidev_sync_unlocked(spidev->spi, &m); } =20 /*------------------------------------------------------------------------= -*/ @@ -157,7 +140,7 @@ static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_p= os) { struct spidev_data *spidev; - ssize_t status; + ssize_t status =3D -ESHUTDOWN; =20 /* chipselect only toggles at start or end of operation */ if (count > bufsiz) @@ -165,7 +148,11 @@ spidev_read(struct file *filp, char __user *buf, size_= t count, loff_t *f_pos) =20 spidev =3D filp->private_data; =20 - mutex_lock(&spidev->buf_lock); + mutex_lock(&spidev->spi_lock); + + if (spidev->spi =3D=3D NULL) + goto err_spi_removed; + status =3D spidev_sync_read(spidev, count); if (status > 0) { unsigned long missing; @@ -176,7 +163,9 @@ spidev_read(struct file *filp, char __user *buf, size_t= count, loff_t *f_pos) else status =3D status - missing; } - mutex_unlock(&spidev->buf_lock); + +err_spi_removed: + mutex_unlock(&spidev->spi_lock); =20 return status; } @@ -187,7 +176,7 @@ spidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; - ssize_t status; + ssize_t status =3D -ESHUTDOWN; unsigned long missing; =20 /* chipselect only toggles at start or end of operation */ @@ -196,13 +185,19 @@ spidev_write(struct file *filp, const char __user *bu= f, =20 spidev =3D filp->private_data; =20 - mutex_lock(&spidev->buf_lock); + mutex_lock(&spidev->spi_lock); + + if (spidev->spi =3D=3D NULL) + goto err_spi_removed; + missing =3D copy_from_user(spidev->tx_buffer, buf, count); if (missing =3D=3D 0) status =3D spidev_sync_write(spidev, count); else status =3D -EFAULT; - mutex_unlock(&spidev->buf_lock); + +err_spi_removed: + mutex_unlock(&spidev->spi_lock); =20 return status; } @@ -376,14 +371,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsi= gned long arg) return -ESHUTDOWN; } =20 - /* use the buffer lock here for triple duty: - * - prevent I/O (from us) so calling spi_setup() is safe; - * - prevent concurrent SPI_IOC_WR_* from morphing - * data fields while SPI_IOC_RD_* reads them; - * - SPI_IOC_MESSAGE needs the buffer locked "normally". - */ - mutex_lock(&spidev->buf_lock); - switch (cmd) { /* read requests */ case SPI_IOC_RD_MODE: @@ -516,7 +503,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsig= ned long arg) break; } =20 - mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); mutex_unlock(&spidev->spi_lock); return retval; @@ -547,9 +533,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned i= nt cmd, return -ESHUTDOWN; } =20 - /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ - mutex_lock(&spidev->buf_lock); - /* Check message and copy into scratch area */ ioc =3D spidev_get_ioc_message(cmd, u_ioc, &n_ioc); if (IS_ERR(ioc)) { @@ -570,7 +553,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned i= nt cmd, kfree(ioc); =20 done: - mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); mutex_unlock(&spidev->spi_lock); return retval; @@ -795,7 +777,6 @@ static int spidev_probe(struct spi_device *spi) /* Initialize the driver data */ spidev->spi =3D spi; mutex_init(&spidev->spi_lock); - mutex_init(&spidev->buf_lock); =20 INIT_LIST_HEAD(&spidev->device_entry); =20 --=20 2.43.0