Skip to content

Commit ae7d234

Browse files
yopebroonie
authored andcommitted
spi: Don't use the message queue if possible in spi_sync
The interaction with the controller message queue and its corresponding auxiliary flags and variables requires the use of the queue_lock which is costly. Since spi_sync will transfer the complete message anyway, and not return until it is finished, there is no need to put the message into the queue if the queue is empty. This can save a lot of overhead. As an example of how significant this is, when using the MCP2518FD SPI CAN controller on a i.MX8MM SoC, the time during which the interrupt line stays active (during 3 relatively short spi_sync messages), is reduced from 98us to 72us by this patch. Signed-off-by: David Jander <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 1714582 commit ae7d234

File tree

2 files changed

+159
-98
lines changed

2 files changed

+159
-98
lines changed

drivers/spi/spi.c

Lines changed: 149 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,80 @@ static void spi_idle_runtime_pm(struct spi_controller *ctlr)
15491549
}
15501550
}
15511551

1552+
static int __spi_pump_transfer_message(struct spi_controller *ctlr,
1553+
struct spi_message *msg, bool was_busy)
1554+
{
1555+
struct spi_transfer *xfer;
1556+
int ret;
1557+
1558+
if (!was_busy && ctlr->auto_runtime_pm) {
1559+
ret = pm_runtime_get_sync(ctlr->dev.parent);
1560+
if (ret < 0) {
1561+
pm_runtime_put_noidle(ctlr->dev.parent);
1562+
dev_err(&ctlr->dev, "Failed to power device: %d\n",
1563+
ret);
1564+
return ret;
1565+
}
1566+
}
1567+
1568+
if (!was_busy)
1569+
trace_spi_controller_busy(ctlr);
1570+
1571+
if (!was_busy && ctlr->prepare_transfer_hardware) {
1572+
ret = ctlr->prepare_transfer_hardware(ctlr);
1573+
if (ret) {
1574+
dev_err(&ctlr->dev,
1575+
"failed to prepare transfer hardware: %d\n",
1576+
ret);
1577+
1578+
if (ctlr->auto_runtime_pm)
1579+
pm_runtime_put(ctlr->dev.parent);
1580+
1581+
msg->status = ret;
1582+
spi_finalize_current_message(ctlr);
1583+
1584+
return ret;
1585+
}
1586+
}
1587+
1588+
trace_spi_message_start(msg);
1589+
1590+
if (ctlr->prepare_message) {
1591+
ret = ctlr->prepare_message(ctlr, msg);
1592+
if (ret) {
1593+
dev_err(&ctlr->dev, "failed to prepare message: %d\n",
1594+
ret);
1595+
msg->status = ret;
1596+
spi_finalize_current_message(ctlr);
1597+
return ret;
1598+
}
1599+
msg->prepared = true;
1600+
}
1601+
1602+
ret = spi_map_msg(ctlr, msg);
1603+
if (ret) {
1604+
msg->status = ret;
1605+
spi_finalize_current_message(ctlr);
1606+
return ret;
1607+
}
1608+
1609+
if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
1610+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
1611+
xfer->ptp_sts_word_pre = 0;
1612+
ptp_read_system_prets(xfer->ptp_sts);
1613+
}
1614+
}
1615+
1616+
ret = ctlr->transfer_one_message(ctlr, msg);
1617+
if (ret) {
1618+
dev_err(&ctlr->dev,
1619+
"failed to transfer one message from queue\n");
1620+
return ret;
1621+
}
1622+
1623+
return 0;
1624+
}
1625+
15521626
/**
15531627
* __spi_pump_messages - function which processes spi message queue
15541628
* @ctlr: controller to process queue for
@@ -1564,7 +1638,6 @@ static void spi_idle_runtime_pm(struct spi_controller *ctlr)
15641638
*/
15651639
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
15661640
{
1567-
struct spi_transfer *xfer;
15681641
struct spi_message *msg;
15691642
bool was_busy = false;
15701643
unsigned long flags;
@@ -1599,6 +1672,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
15991672
!ctlr->unprepare_transfer_hardware) {
16001673
spi_idle_runtime_pm(ctlr);
16011674
ctlr->busy = false;
1675+
ctlr->queue_empty = true;
16021676
trace_spi_controller_idle(ctlr);
16031677
} else {
16041678
kthread_queue_work(ctlr->kworker,
@@ -1625,6 +1699,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
16251699

16261700
spin_lock_irqsave(&ctlr->queue_lock, flags);
16271701
ctlr->idling = false;
1702+
ctlr->queue_empty = true;
16281703
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
16291704
return;
16301705
}
@@ -1641,75 +1716,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
16411716
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
16421717

16431718
mutex_lock(&ctlr->io_mutex);
1644-
1645-
if (!was_busy && ctlr->auto_runtime_pm) {
1646-
ret = pm_runtime_resume_and_get(ctlr->dev.parent);
1647-
if (ret < 0) {
1648-
dev_err(&ctlr->dev, "Failed to power device: %d\n",
1649-
ret);
1650-
mutex_unlock(&ctlr->io_mutex);
1651-
return;
1652-
}
1653-
}
1654-
1655-
if (!was_busy)
1656-
trace_spi_controller_busy(ctlr);
1657-
1658-
if (!was_busy && ctlr->prepare_transfer_hardware) {
1659-
ret = ctlr->prepare_transfer_hardware(ctlr);
1660-
if (ret) {
1661-
dev_err(&ctlr->dev,
1662-
"failed to prepare transfer hardware: %d\n",
1663-
ret);
1664-
1665-
if (ctlr->auto_runtime_pm)
1666-
pm_runtime_put(ctlr->dev.parent);
1667-
1668-
msg->status = ret;
1669-
spi_finalize_current_message(ctlr);
1670-
1671-
mutex_unlock(&ctlr->io_mutex);
1672-
return;
1673-
}
1674-
}
1675-
1676-
trace_spi_message_start(msg);
1677-
1678-
if (ctlr->prepare_message) {
1679-
ret = ctlr->prepare_message(ctlr, msg);
1680-
if (ret) {
1681-
dev_err(&ctlr->dev, "failed to prepare message: %d\n",
1682-
ret);
1683-
msg->status = ret;
1684-
spi_finalize_current_message(ctlr);
1685-
goto out;
1686-
}
1687-
msg->prepared = true;
1688-
}
1689-
1690-
ret = spi_map_msg(ctlr, msg);
1691-
if (ret) {
1692-
msg->status = ret;
1693-
spi_finalize_current_message(ctlr);
1694-
goto out;
1695-
}
1696-
1697-
if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
1698-
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
1699-
xfer->ptp_sts_word_pre = 0;
1700-
ptp_read_system_prets(xfer->ptp_sts);
1701-
}
1702-
}
1703-
1704-
ret = ctlr->transfer_one_message(ctlr, msg);
1705-
if (ret) {
1706-
dev_err(&ctlr->dev,
1707-
"failed to transfer one message from queue: %d\n",
1708-
ret);
1709-
goto out;
1710-
}
1711-
1712-
out:
1719+
ret = __spi_pump_transfer_message(ctlr, msg, was_busy);
17131720
mutex_unlock(&ctlr->io_mutex);
17141721

17151722
/* Prod the scheduler in case transfer_one() was busy waiting */
@@ -1839,6 +1846,7 @@ static int spi_init_queue(struct spi_controller *ctlr)
18391846
{
18401847
ctlr->running = false;
18411848
ctlr->busy = false;
1849+
ctlr->queue_empty = true;
18421850

18431851
ctlr->kworker = kthread_create_worker(0, dev_name(&ctlr->dev));
18441852
if (IS_ERR(ctlr->kworker)) {
@@ -1936,11 +1944,20 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
19361944

19371945
mesg->prepared = false;
19381946

1939-
spin_lock_irqsave(&ctlr->queue_lock, flags);
1940-
ctlr->cur_msg = NULL;
1941-
ctlr->fallback = false;
1942-
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
1943-
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
1947+
if (!mesg->sync) {
1948+
/*
1949+
* This message was sent via the async message queue. Handle
1950+
* the queue and kick the worker thread to do the
1951+
* idling/shutdown or send the next message if needed.
1952+
*/
1953+
spin_lock_irqsave(&ctlr->queue_lock, flags);
1954+
WARN(ctlr->cur_msg != mesg,
1955+
"Finalizing queued message that is not the current head of queue!");
1956+
ctlr->cur_msg = NULL;
1957+
ctlr->fallback = false;
1958+
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
1959+
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
1960+
}
19441961

19451962
trace_spi_message_done(mesg);
19461963

@@ -2043,6 +2060,7 @@ static int __spi_queued_transfer(struct spi_device *spi,
20432060
msg->status = -EINPROGRESS;
20442061

20452062
list_add_tail(&msg->queue, &ctlr->queue);
2063+
ctlr->queue_empty = false;
20462064
if (!ctlr->busy && need_pump)
20472065
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
20482066

@@ -3938,6 +3956,39 @@ static int spi_async_locked(struct spi_device *spi, struct spi_message *message)
39383956

39393957
}
39403958

3959+
static void __spi_transfer_message_noqueue(struct spi_controller *ctlr, struct spi_message *msg)
3960+
{
3961+
bool was_busy;
3962+
int ret;
3963+
3964+
mutex_lock(&ctlr->io_mutex);
3965+
3966+
/* If another context is idling the device then wait */
3967+
while (ctlr->idling)
3968+
usleep_range(10000, 11000);
3969+
3970+
was_busy = READ_ONCE(ctlr->busy);
3971+
3972+
ret = __spi_pump_transfer_message(ctlr, msg, was_busy);
3973+
if (ret)
3974+
goto out;
3975+
3976+
if (!was_busy) {
3977+
kfree(ctlr->dummy_rx);
3978+
ctlr->dummy_rx = NULL;
3979+
kfree(ctlr->dummy_tx);
3980+
ctlr->dummy_tx = NULL;
3981+
if (ctlr->unprepare_transfer_hardware &&
3982+
ctlr->unprepare_transfer_hardware(ctlr))
3983+
dev_err(&ctlr->dev,
3984+
"failed to unprepare transfer hardware\n");
3985+
spi_idle_runtime_pm(ctlr);
3986+
}
3987+
3988+
out:
3989+
mutex_unlock(&ctlr->io_mutex);
3990+
}
3991+
39413992
/*-------------------------------------------------------------------------*/
39423993

39433994
/*
@@ -3956,51 +4007,52 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message)
39564007
DECLARE_COMPLETION_ONSTACK(done);
39574008
int status;
39584009
struct spi_controller *ctlr = spi->controller;
3959-
unsigned long flags;
39604010

39614011
status = __spi_validate(spi, message);
39624012
if (status != 0)
39634013
return status;
39644014

3965-
message->complete = spi_complete;
3966-
message->context = &done;
39674015
message->spi = spi;
39684016

39694017
SPI_STATISTICS_INCREMENT_FIELD(ctlr->pcpu_statistics, spi_sync);
39704018
SPI_STATISTICS_INCREMENT_FIELD(spi->pcpu_statistics, spi_sync);
39714019

39724020
/*
3973-
* If we're not using the legacy transfer method then we will
3974-
* try to transfer in the calling context so special case.
3975-
* This code would be less tricky if we could remove the
3976-
* support for driver implemented message queues.
4021+
* Checking queue_empty here only guarantees async/sync message
4022+
* ordering when coming from the same context. It does not need to
4023+
* guard against reentrancy from a different context. The io_mutex
4024+
* will catch those cases.
39774025
*/
3978-
if (ctlr->transfer == spi_queued_transfer) {
3979-
spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);
4026+
if (READ_ONCE(ctlr->queue_empty)) {
4027+
message->sync = true;
4028+
message->actual_length = 0;
4029+
message->status = -EINPROGRESS;
39804030

39814031
trace_spi_message_submit(message);
39824032

3983-
status = __spi_queued_transfer(spi, message, false);
4033+
SPI_STATISTICS_INCREMENT_FIELD(ctlr->pcpu_statistics, spi_sync_immediate);
4034+
SPI_STATISTICS_INCREMENT_FIELD(spi->pcpu_statistics, spi_sync_immediate);
39844035

3985-
spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
3986-
} else {
3987-
status = spi_async_locked(spi, message);
4036+
__spi_transfer_message_noqueue(ctlr, message);
4037+
4038+
return message->status;
39884039
}
39894040

4041+
/*
4042+
* There are messages in the async queue that could have originated
4043+
* from the same context, so we need to preserve ordering.
4044+
* Therefor we send the message to the async queue and wait until they
4045+
* are completed.
4046+
*/
4047+
message->complete = spi_complete;
4048+
message->context = &done;
4049+
status = spi_async_locked(spi, message);
39904050
if (status == 0) {
3991-
/* Push out the messages in the calling context if we can */
3992-
if (ctlr->transfer == spi_queued_transfer) {
3993-
SPI_STATISTICS_INCREMENT_FIELD(ctlr->pcpu_statistics,
3994-
spi_sync_immediate);
3995-
SPI_STATISTICS_INCREMENT_FIELD(spi->pcpu_statistics,
3996-
spi_sync_immediate);
3997-
__spi_pump_messages(ctlr, false);
3998-
}
3999-
40004051
wait_for_completion(&done);
40014052
status = message->status;
40024053
}
40034054
message->context = NULL;
4055+
40044056
return status;
40054057
}
40064058

include/linux/spi/spi.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
461461
* @irq_flags: Interrupt enable state during PTP system timestamping
462462
* @fallback: fallback to pio if dma transfer return failure with
463463
* SPI_TRANS_FAIL_NO_START.
464+
* @queue_empty: signal green light for opportunistically skipping the queue
465+
* for spi_sync transfers.
464466
*
465467
* Each SPI controller can communicate with one or more @spi_device
466468
* children. These make a small bus, sharing MOSI, MISO and SCK signals
@@ -677,6 +679,9 @@ struct spi_controller {
677679

678680
/* Interrupt enable state during PTP system timestamping */
679681
unsigned long irq_flags;
682+
683+
/* Flag for enabling opportunistic skipping of the queue in spi_sync */
684+
bool queue_empty;
680685
};
681686

682687
static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
@@ -986,6 +991,7 @@ struct spi_transfer {
986991
* @state: for use by whichever driver currently owns the message
987992
* @resources: for resource management when the spi message is processed
988993
* @prepared: spi_prepare_message was called for the this message
994+
* @sync: this message took the direct sync path skipping the async queue
989995
*
990996
* A @spi_message is used to execute an atomic sequence of data transfers,
991997
* each represented by a struct spi_transfer. The sequence is "atomic"
@@ -1037,7 +1043,10 @@ struct spi_message {
10371043
struct list_head resources;
10381044

10391045
/* spi_prepare_message was called for this message */
1040-
bool prepared;
1046+
bool prepared;
1047+
1048+
/* this message is skipping the async queue */
1049+
bool sync;
10411050
};
10421051

10431052
static inline void spi_message_init_no_memset(struct spi_message *m)

0 commit comments

Comments
 (0)