Skip to content
/ linux Public

Commit f8431b8

Browse files
ef-geSasha Levin
authored andcommitted
spi: spidev: fix lock inversion between spi_lock and buf_lock
[ Upstream commit 40534d1 ] 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 <fg@emlix.com> Link: https://patch.msgid.link/20260211072616.489522-1-fg@emlix.com Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent b701db9 commit f8431b8

File tree

1 file changed

+22
-41
lines changed

1 file changed

+22
-41
lines changed

drivers/spi/spidev.c

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ struct spidev_data {
7474
struct list_head device_entry;
7575

7676
/* TX/RX buffers are NULL unless this device is open (users > 0) */
77-
struct mutex buf_lock;
7877
unsigned users;
7978
u8 *tx_buffer;
8079
u8 *rx_buffer;
@@ -102,24 +101,6 @@ spidev_sync_unlocked(struct spi_device *spi, struct spi_message *message)
102101
return status;
103102
}
104103

105-
static ssize_t
106-
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
107-
{
108-
ssize_t status;
109-
struct spi_device *spi;
110-
111-
mutex_lock(&spidev->spi_lock);
112-
spi = spidev->spi;
113-
114-
if (spi == NULL)
115-
status = -ESHUTDOWN;
116-
else
117-
status = spidev_sync_unlocked(spi, message);
118-
119-
mutex_unlock(&spidev->spi_lock);
120-
return status;
121-
}
122-
123104
static inline ssize_t
124105
spidev_sync_write(struct spidev_data *spidev, size_t len)
125106
{
@@ -132,7 +113,8 @@ spidev_sync_write(struct spidev_data *spidev, size_t len)
132113

133114
spi_message_init(&m);
134115
spi_message_add_tail(&t, &m);
135-
return spidev_sync(spidev, &m);
116+
117+
return spidev_sync_unlocked(spidev->spi, &m);
136118
}
137119

138120
static inline ssize_t
@@ -147,7 +129,8 @@ spidev_sync_read(struct spidev_data *spidev, size_t len)
147129

148130
spi_message_init(&m);
149131
spi_message_add_tail(&t, &m);
150-
return spidev_sync(spidev, &m);
132+
133+
return spidev_sync_unlocked(spidev->spi, &m);
151134
}
152135

153136
/*-------------------------------------------------------------------------*/
@@ -157,15 +140,19 @@ static ssize_t
157140
spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
158141
{
159142
struct spidev_data *spidev;
160-
ssize_t status;
143+
ssize_t status = -ESHUTDOWN;
161144

162145
/* chipselect only toggles at start or end of operation */
163146
if (count > bufsiz)
164147
return -EMSGSIZE;
165148

166149
spidev = filp->private_data;
167150

168-
mutex_lock(&spidev->buf_lock);
151+
mutex_lock(&spidev->spi_lock);
152+
153+
if (spidev->spi == NULL)
154+
goto err_spi_removed;
155+
169156
status = spidev_sync_read(spidev, count);
170157
if (status > 0) {
171158
unsigned long missing;
@@ -176,7 +163,9 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
176163
else
177164
status = status - missing;
178165
}
179-
mutex_unlock(&spidev->buf_lock);
166+
167+
err_spi_removed:
168+
mutex_unlock(&spidev->spi_lock);
180169

181170
return status;
182171
}
@@ -187,7 +176,7 @@ spidev_write(struct file *filp, const char __user *buf,
187176
size_t count, loff_t *f_pos)
188177
{
189178
struct spidev_data *spidev;
190-
ssize_t status;
179+
ssize_t status = -ESHUTDOWN;
191180
unsigned long missing;
192181

193182
/* chipselect only toggles at start or end of operation */
@@ -196,13 +185,19 @@ spidev_write(struct file *filp, const char __user *buf,
196185

197186
spidev = filp->private_data;
198187

199-
mutex_lock(&spidev->buf_lock);
188+
mutex_lock(&spidev->spi_lock);
189+
190+
if (spidev->spi == NULL)
191+
goto err_spi_removed;
192+
200193
missing = copy_from_user(spidev->tx_buffer, buf, count);
201194
if (missing == 0)
202195
status = spidev_sync_write(spidev, count);
203196
else
204197
status = -EFAULT;
205-
mutex_unlock(&spidev->buf_lock);
198+
199+
err_spi_removed:
200+
mutex_unlock(&spidev->spi_lock);
206201

207202
return status;
208203
}
@@ -379,14 +374,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
379374

380375
ctlr = spi->controller;
381376

382-
/* use the buffer lock here for triple duty:
383-
* - prevent I/O (from us) so calling spi_setup() is safe;
384-
* - prevent concurrent SPI_IOC_WR_* from morphing
385-
* data fields while SPI_IOC_RD_* reads them;
386-
* - SPI_IOC_MESSAGE needs the buffer locked "normally".
387-
*/
388-
mutex_lock(&spidev->buf_lock);
389-
390377
switch (cmd) {
391378
/* read requests */
392379
case SPI_IOC_RD_MODE:
@@ -510,7 +497,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
510497
break;
511498
}
512499

513-
mutex_unlock(&spidev->buf_lock);
514500
spi_dev_put(spi);
515501
mutex_unlock(&spidev->spi_lock);
516502
return retval;
@@ -541,9 +527,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
541527
return -ESHUTDOWN;
542528
}
543529

544-
/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
545-
mutex_lock(&spidev->buf_lock);
546-
547530
/* Check message and copy into scratch area */
548531
ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
549532
if (IS_ERR(ioc)) {
@@ -564,7 +547,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
564547
kfree(ioc);
565548

566549
done:
567-
mutex_unlock(&spidev->buf_lock);
568550
spi_dev_put(spi);
569551
mutex_unlock(&spidev->spi_lock);
570552
return retval;
@@ -790,7 +772,6 @@ static int spidev_probe(struct spi_device *spi)
790772
/* Initialize the driver data */
791773
spidev->spi = spi;
792774
mutex_init(&spidev->spi_lock);
793-
mutex_init(&spidev->buf_lock);
794775

795776
INIT_LIST_HEAD(&spidev->device_entry);
796777

0 commit comments

Comments
 (0)