* [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout
@ 2026-09-22 16:02 Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-09-22 16:02 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
Cc: Adrian Ng Ho Yin
Stratix10 FPGA reconfiguration can fail or hang for three related
reasons in the FPGA manager. This series hardens s10_ops_write() and
s10_ops_write_complete() so those paths cannot skip a completion wait,
treat BUFFER_DONE | ERROR as success, or return immediately when the
DT-supplied config_complete_timeout_us is left at 0. No dependency on
the stratix10-svc accumulate changes.
1) always wait for svc buffer completion
s10_ops_write() skips wait_for_completion_timeout() when
priv->status is already set. That skip is unnecessary and unsafe:
the callback may fire between the CLAIM/SUBMIT send and the wait
check. Always call the wait; if complete() already ran, the done
counter is raised and the wait returns immediately.
2) prefer ERROR over BUFFER_DONE
BUFFER_DONE is checked before ERROR, so a combined
BUFFER_DONE | ERROR status is treated as success. Check ERROR
first so that status aborts the write. Also clear priv->status at
the start of write_init so stale bits from an aborted transfer
cannot leak into the next reconfiguration.
3) floor config_complete_timeout_us
of-fpga-region leaves config_complete_timeout_us at 0 when the DT
property is absent. usecs_to_jiffies(0) makes write_complete return
immediately, while reconfiguration takes ~600ms in practice.
Enforce S10_RECONFIG_TIMEOUT as a minimum floor while still
honouring any larger caller-supplied value. Note this prevents a
DT property from lowering the timeout below that floor.
Adrian Ng Ho Yin (3):
fpga: stratix10-soc: always wait for svc buffer completion
fpga: stratix10-soc: prefer ERROR over BUFFER_DONE
fpga: stratix10-soc: floor config_complete_timeout_us
drivers/fpga/stratix10-soc.c | 41 ++++++++++++++++++++++++------------
1 file changed, 27 insertions(+), 14 deletions(-)
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] fpga: stratix10-soc: always wait for svc buffer completion
2026-09-22 16:02 [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
@ 2026-09-22 16:02 ` Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE Adrian Ng Ho Yin
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-09-22 16:02 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
Cc: Adrian Ng Ho Yin
Always call wait_for_completion_timeout() in s10_ops_write(). Skipping
the wait when priv->status is already set is unnecessary: if the
callback already ran, complete() raised the done counter and the wait
returns immediately.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
drivers/fpga/stratix10-soc.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index b8ec2e6f615f..3ba0ffa1d9e0 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -310,14 +310,14 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
}
/*
- * If callback hasn't already happened, wait for buffers to be
- * returned from service layer
+ * Always wait for the completion rather than skipping it when
+ * priv->status is already set. The callback may have fired
+ * between the CLAIM/SUBMIT send and here; if so, complete()
+ * has already incremented the done counter and
+ * wait_for_completion_timeout() returns immediately.
*/
- wait_status = 1; /* not timed out */
- if (!priv->status)
- wait_status = wait_for_completion_timeout(
- &priv->status_return_completion,
- S10_BUFFER_TIMEOUT);
+ wait_status = wait_for_completion_timeout(
+ &priv->status_return_completion, S10_BUFFER_TIMEOUT);
if (test_and_clear_bit(SVC_STATUS_BUFFER_DONE, &priv->status) ||
test_and_clear_bit(SVC_STATUS_BUFFER_SUBMITTED,
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE
2026-09-22 16:02 [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
@ 2026-09-22 16:02 ` Adrian Ng Ho Yin
2026-09-28 11:59 ` Xu Yilun
2026-09-22 16:02 ` [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
3 siblings, 1 reply; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-09-22 16:02 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
Cc: Adrian Ng Ho Yin
Check SVC_STATUS_ERROR before BUFFER_DONE so a combined
BUFFER_DONE | ERROR callback aborts instead of being treated as success.
Clear priv->status at the start of write_init so stale bits cannot leak
into the next reconfiguration.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
drivers/fpga/stratix10-soc.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 3ba0ffa1d9e0..c16ed571630b 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -183,6 +183,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
uint i;
int ret;
+ /* Drop stale status bits left by a previous aborted write. */
+ priv->status = 0;
+
ctype.flags = 0;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_dbg(dev, "Requesting partial reconfiguration.\n");
@@ -319,6 +322,13 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
wait_status = wait_for_completion_timeout(
&priv->status_return_completion, S10_BUFFER_TIMEOUT);
+ /* ERROR must win over BUFFER_DONE when both are set. */
+ if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
+ dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
+ ret = -EFAULT;
+ break;
+ }
+
if (test_and_clear_bit(SVC_STATUS_BUFFER_DONE, &priv->status) ||
test_and_clear_bit(SVC_STATUS_BUFFER_SUBMITTED,
&priv->status)) {
@@ -326,12 +336,6 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
continue;
}
- if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
- dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
- ret = -EFAULT;
- break;
- }
-
if (!wait_status) {
dev_err(dev, "timeout waiting for svc layer buffers\n");
ret = -ETIMEDOUT;
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us
2026-09-22 16:02 [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE Adrian Ng Ho Yin
@ 2026-09-22 16:02 ` Adrian Ng Ho Yin
2026-09-28 12:14 ` Xu Yilun
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
3 siblings, 1 reply; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-09-22 16:02 UTC (permalink / raw)
To: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
Cc: Adrian Ng Ho Yin
Keep info->config_complete_timeout_us configurable. of-fpga-region
leaves it at 0 when the DT property is absent, and usecs_to_jiffies(0)
makes the wait return immediately. Reconfiguration also takes ~600ms in
practice, so enforce S10_RECONFIG_TIMEOUT as a minimum floor while still
honouring any larger caller-supplied value. Note this prevents a DT
property from lowering the timeout below that floor.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
drivers/fpga/stratix10-soc.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index c16ed571630b..83590e6b405f 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -7,6 +7,7 @@
#include <linux/completion.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -360,7 +361,15 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
unsigned long timeout;
int ret;
- timeout = usecs_to_jiffies(info->config_complete_timeout_us);
+ /*
+ * Keep config_complete_timeout_us configurable. of-fpga-region leaves
+ * it at 0 when the DT property is absent, and usecs_to_jiffies(0)
+ * makes the wait return immediately. Reconfiguration also takes
+ * ~600ms in practice, so enforce S10_RECONFIG_TIMEOUT as a minimum
+ * floor while still honouring any larger caller-supplied value.
+ */
+ timeout = max(usecs_to_jiffies(info->config_complete_timeout_us),
+ S10_RECONFIG_TIMEOUT);
do {
reinit_completion(&priv->status_return_completion);
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE
2026-09-22 16:02 ` [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE Adrian Ng Ho Yin
@ 2026-09-28 11:59 ` Xu Yilun
0 siblings, 0 replies; 10+ messages in thread
From: Xu Yilun @ 2026-09-28 11:59 UTC (permalink / raw)
To: Adrian Ng Ho Yin
Cc: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
On Wed, Sep 23, 2026 at 12:02:42AM +0800, Adrian Ng Ho Yin wrote:
> Check SVC_STATUS_ERROR before BUFFER_DONE so a combined
> BUFFER_DONE | ERROR callback aborts instead of being treated as success.
> Clear priv->status at the start of write_init so stale bits cannot leak
> into the next reconfiguration.
There is also a COMPLETED | ERROR combination, does it need the same
handling?
>
> Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
> ---
> drivers/fpga/stratix10-soc.c | 16 ++++++++++------
> 1 file changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> index 3ba0ffa1d9e0..c16ed571630b 100644
> --- a/drivers/fpga/stratix10-soc.c
> +++ b/drivers/fpga/stratix10-soc.c
> @@ -183,6 +183,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
> uint i;
> int ret;
>
> + /* Drop stale status bits left by a previous aborted write. */
> + priv->status = 0;
> +
> ctype.flags = 0;
> if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
> dev_dbg(dev, "Requesting partial reconfiguration.\n");
> @@ -319,6 +322,13 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
> wait_status = wait_for_completion_timeout(
> &priv->status_return_completion, S10_BUFFER_TIMEOUT);
>
> + /* ERROR must win over BUFFER_DONE when both are set. */
> + if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
> + dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
> + ret = -EFAULT;
> + break;
> + }
> +
> if (test_and_clear_bit(SVC_STATUS_BUFFER_DONE, &priv->status) ||
> test_and_clear_bit(SVC_STATUS_BUFFER_SUBMITTED,
> &priv->status)) {
> @@ -326,12 +336,6 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
> continue;
> }
>
> - if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
> - dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
> - ret = -EFAULT;
> - break;
> - }
> -
I think these command handlings in this series are much of the same
style:
- init a completion
- send command
- wait_for_completion_timeout
- check error status
- check successful status
- stratix10_svc_done
Now they are really a mess, hardly reviewable. Is it possible we make a
helper to regulate them?
> if (!wait_status) {
> dev_err(dev, "timeout waiting for svc layer buffers\n");
> ret = -ETIMEDOUT;
> --
> 2.49.GIT
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us
2026-09-22 16:02 ` [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us Adrian Ng Ho Yin
@ 2026-09-28 12:14 ` Xu Yilun
0 siblings, 0 replies; 10+ messages in thread
From: Xu Yilun @ 2026-09-28 12:14 UTC (permalink / raw)
To: Adrian Ng Ho Yin
Cc: Moritz Fischer, Xu Yilun, Tom Rix, linux-fpga, linux-kernel
On Wed, Sep 23, 2026 at 12:02:43AM +0800, Adrian Ng Ho Yin wrote:
> Keep info->config_complete_timeout_us configurable. of-fpga-region
> leaves it at 0 when the DT property is absent, and usecs_to_jiffies(0)
> makes the wait return immediately. Reconfiguration also takes ~600ms in
> practice, so enforce S10_RECONFIG_TIMEOUT as a minimum floor while still
> honouring any larger caller-supplied value. Note this prevents a DT
> property from lowering the timeout below that floor.
>
> Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
> ---
> drivers/fpga/stratix10-soc.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> index c16ed571630b..83590e6b405f 100644
> --- a/drivers/fpga/stratix10-soc.c
> +++ b/drivers/fpga/stratix10-soc.c
> @@ -7,6 +7,7 @@
> #include <linux/completion.h>
> #include <linux/fpga/fpga-mgr.h>
> #include <linux/firmware/intel/stratix10-svc-client.h>
> +#include <linux/minmax.h>
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_platform.h>
> @@ -360,7 +361,15 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
> unsigned long timeout;
> int ret;
>
> - timeout = usecs_to_jiffies(info->config_complete_timeout_us);
> + /*
> + * Keep config_complete_timeout_us configurable. of-fpga-region leaves
> + * it at 0 when the DT property is absent, and usecs_to_jiffies(0)
> + * makes the wait return immediately. Reconfiguration also takes
> + * ~600ms in practice, so enforce S10_RECONFIG_TIMEOUT as a minimum
> + * floor while still honouring any larger caller-supplied value.
> + */
> + timeout = max(usecs_to_jiffies(info->config_complete_timeout_us),
> + S10_RECONFIG_TIMEOUT);
Basically I think config_complete_timeout_us is used in a wrong way. You
can refer to other vendor fpga driver for reference.
>
> do {
> reinit_completion(&priv->status_return_completion);
> --
> 2.49.GIT
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout
2026-09-22 16:02 [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
` (2 preceding siblings ...)
2026-09-22 16:02 ` [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us Adrian Ng Ho Yin
@ 2026-10-20 7:49 ` Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
` (2 more replies)
3 siblings, 3 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-10-20 7:49 UTC (permalink / raw)
To: mdf, yilun.xu, yilun.xu, trix, linux-fpga, linux-kernel; +Cc: Adrian Ng Ho Yin
Stratix10 FPGA reconfiguration can fail or hang for three related
reasons in the FPGA manager. This series hardens s10_ops_write_init(),
s10_ops_write() and s10_ops_write_complete(). No dependency on the
stratix10-svc accumulate changes.
1) always wait for svc buffer completion
s10_ops_write() skips wait_for_completion_timeout() when
priv->status is already set. Always call the wait; if complete()
already ran, the done counter is raised and the wait returns
immediately.
2) use config_complete_timeout_us as a deadline
config_complete_timeout_us was passed as the timeout of every wait
for a service layer reply, so the value 0 that of-fpga-region
leaves when the DT property is absent fails write_complete at once.
Wait for each RECONFIG_STATUS reply with a driver timeout that
covers the service layer's own polling window, and use
config_complete_timeout_us only as the deadline for re-polling
after a BUSY reply, as xilinx-core, efinix-spi and
altera-pr-ip-core do. Because one reply can take up to the
service layer polling window, the deadline is coarse.
3) SVC wait helper; prefer ERROR over success bits
Add s10_svc_wait() / s10_svc_send_and_wait(). write_init becomes
one s10_svc_send_and_wait() call, write_complete uses it for each
status request, and write() uses s10_svc_wait() after sending a
data buffer or claim request. ERROR takes precedence over
BUFFER_DONE, BUFFER_SUBMITTED, COMPLETED and OK in the accumulated
status word.
Changes in v3:
- Patch 2 replaces "floor config_complete_timeout_us": drop the max()
floor, use config_complete_timeout_us as a re-poll deadline instead
of the per-reply wait (Xu Yilun)
- Patch 3 replaces "prefer ERROR over BUFFER_DONE": add the wait
helpers and convert all three ops to them (Xu Yilun)
- ERROR also wins over COMPLETED and OK (Xu Yilun)
- Timeout patch moved before the helper patch so no intermediate
commit can re-poll without a bound
- write_complete ignores a stray wake-up from the last claim callback
instead of counting it against the deadline
- v2 was posted without a version tag as "[PATCH n/3]"
Adrian Ng Ho Yin (3):
fpga: stratix10-soc: always wait for svc buffer completion
fpga: stratix10-soc: use config_complete_timeout_us as a deadline
fpga: stratix10-soc: add SVC wait helper and prefer ERROR
drivers/fpga/stratix10-soc.c | 178 +++++++++++++++++++++--------------
1 file changed, 106 insertions(+), 72 deletions(-)
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/3] fpga: stratix10-soc: always wait for svc buffer completion
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
@ 2026-10-20 7:49 ` Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 2/3] fpga: stratix10-soc: use config_complete_timeout_us as a deadline Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 3/3] fpga: stratix10-soc: add SVC wait helper and prefer ERROR Adrian Ng Ho Yin
2 siblings, 0 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-10-20 7:49 UTC (permalink / raw)
To: mdf, yilun.xu, yilun.xu, trix, linux-fpga, linux-kernel; +Cc: Adrian Ng Ho Yin
Always call wait_for_completion_timeout() in s10_ops_write(). Skipping
the wait when priv->status is already set is unnecessary: if the
callback already ran, complete() raised the done counter and the wait
returns immediately.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
Notes:
Changes in v3:
- None.
drivers/fpga/stratix10-soc.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index b8ec2e6f615f..3ba0ffa1d9e0 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -310,14 +310,14 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
}
/*
- * If callback hasn't already happened, wait for buffers to be
- * returned from service layer
+ * Always wait for the completion rather than skipping it when
+ * priv->status is already set. The callback may have fired
+ * between the CLAIM/SUBMIT send and here; if so, complete()
+ * has already incremented the done counter and
+ * wait_for_completion_timeout() returns immediately.
*/
- wait_status = 1; /* not timed out */
- if (!priv->status)
- wait_status = wait_for_completion_timeout(
- &priv->status_return_completion,
- S10_BUFFER_TIMEOUT);
+ wait_status = wait_for_completion_timeout(
+ &priv->status_return_completion, S10_BUFFER_TIMEOUT);
if (test_and_clear_bit(SVC_STATUS_BUFFER_DONE, &priv->status) ||
test_and_clear_bit(SVC_STATUS_BUFFER_SUBMITTED,
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 2/3] fpga: stratix10-soc: use config_complete_timeout_us as a deadline
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
@ 2026-10-20 7:49 ` Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 3/3] fpga: stratix10-soc: add SVC wait helper and prefer ERROR Adrian Ng Ho Yin
2 siblings, 0 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-10-20 7:49 UTC (permalink / raw)
To: mdf, yilun.xu, yilun.xu, trix, linux-fpga, linux-kernel; +Cc: Adrian Ng Ho Yin
config_complete_timeout_us is the maximum time the FPGA may take to
enter user mode after programming. Other FPGA managers (xilinx-core,
efinix-spi, altera-pr-ip-core) turn it into a deadline for their own
"is configuration done" polling. s10_ops_write_complete() instead
passes it as the timeout of every wait for a service layer reply.
That couples two unrelated timeouts. of-fpga-region leaves the field at
0 when the DT property is absent, so usecs_to_jiffies(0) makes the very
first wait time out before the service layer has answered, and
write_complete fails even though configuration succeeds.
For COMMAND_RECONFIG_STATUS the service layer polls the firmware for up
to 30 seconds before it replies with COMPLETED, ERROR or BUSY. Wait for
that reply with a driver timeout that covers the polling window
(SVC_COMPLETED_TIMEOUT_MS plus the usual request timeout), and use
config_complete_timeout_us only as the deadline for re-sending the
status request after a BUSY reply. A value of 0 therefore results in a
single status request, as in the other drivers.
The deadline is only checked between replies, and one reply can take up
to the service layer polling window, so values shorter than that behave
like a single request and the deadline can be exceeded by up to one
reply wait. The service layer cannot be interrupted while it polls:
stratix10_svc_done() waits for its thread to finish.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
Notes:
Changes in v3:
- Replaces "floor config_complete_timeout_us". Drop the max() floor and
stop using config_complete_timeout_us as the per-reply wait. Wait for
each RECONFIG_STATUS reply with a driver timeout that covers the
service layer polling window, and use config_complete_timeout_us as
the deadline for re-polling after BUSY, as other FPGA managers do
(Xu Yilun).
- Moved before the helper patch so that no intermediate commit can
re-poll without a bound.
drivers/fpga/stratix10-soc.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 3ba0ffa1d9e0..a69f63f0236b 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -25,6 +25,13 @@
#define S10_BUFFER_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_BUFFER_TIMEOUT_MS))
#define S10_RECONFIG_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_REQUEST_TIMEOUT_MS))
+/*
+ * The service layer polls the configuration status for up to 30 seconds
+ * (SVC_COMPLETED_TIMEOUT_MS) before it replies to COMMAND_RECONFIG_STATUS.
+ */
+#define S10_RECONFIG_STATUS_TIMEOUT \
+ (msecs_to_jiffies(SVC_COMPLETED_TIMEOUT_MS) + S10_RECONFIG_TIMEOUT)
+
/*
* struct s10_svc_buf
* buf: virtual address of buf provided by service layer
@@ -353,10 +360,10 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
{
struct s10_priv *priv = mgr->priv;
struct device *dev = priv->client.dev;
- unsigned long timeout;
+ unsigned long deadline;
int ret;
- timeout = usecs_to_jiffies(info->config_complete_timeout_us);
+ deadline = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
do {
reinit_completion(&priv->status_return_completion);
@@ -365,17 +372,12 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
if (ret < 0)
break;
- ret = wait_for_completion_timeout(
- &priv->status_return_completion, timeout);
- if (!ret) {
- dev_err(dev,
- "timeout waiting for RECONFIG_COMPLETED\n");
+ if (!wait_for_completion_timeout(&priv->status_return_completion,
+ S10_RECONFIG_STATUS_TIMEOUT)) {
+ dev_err(dev, "timeout waiting for RECONFIG_STATUS\n");
ret = -ETIMEDOUT;
break;
}
- /* Not error or timeout, so ret is # of jiffies until timeout */
- timeout = ret;
- ret = 0;
if (test_and_clear_bit(SVC_STATUS_COMPLETED, &priv->status))
break;
@@ -385,6 +387,13 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
ret = -EFAULT;
break;
}
+
+ if (time_after_eq(jiffies, deadline)) {
+ dev_err(dev,
+ "timeout waiting for RECONFIG_COMPLETED\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
} while (1);
stratix10_svc_done(priv->chan);
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 3/3] fpga: stratix10-soc: add SVC wait helper and prefer ERROR
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 2/3] fpga: stratix10-soc: use config_complete_timeout_us as a deadline Adrian Ng Ho Yin
@ 2026-10-20 7:49 ` Adrian Ng Ho Yin
2 siblings, 0 replies; 10+ messages in thread
From: Adrian Ng Ho Yin @ 2026-10-20 7:49 UTC (permalink / raw)
To: mdf, yilun.xu, yilun.xu, trix, linux-fpga, linux-kernel; +Cc: Adrian Ng Ho Yin
write_init, write and write_complete each open-code the same sequence:
reinit the completion, send a service layer command, wait for the reply,
then test status bits. The order of the status tests differs between the
call sites.
The receive callback ORs each reply's status into priv->status, so bits
from more than one reply can be pending when a caller tests them (for
example several BUFFER_DONE callbacks for one RECONFIG_DATA_CLAIM, or a
reply that arrived after an earlier wait gave up). write() and
write_complete() test a success bit before SVC_STATUS_ERROR, so a status
word holding BUFFER_DONE | ERROR or COMPLETED | ERROR is treated as
success. write_init() ignores ERROR when OK is also set.
Add s10_svc_wait(), which waits for a reply and decodes priv->status with
SVC_STATUS_ERROR taking precedence over the caller's success bits, and
s10_svc_send_and_wait(), which also reinitialises the completion and
sends the command. write_init and write_complete use
s10_svc_send_and_wait(); write() sends either a data buffer or a claim
request and uses s10_svc_wait(). The helpers return 0, -EFAULT, -EBUSY
(SVC_STATUS_BUSY), -ETIMEDOUT, or -EAGAIN for a wake-up that carried
none of those bits. The status word is read and cleared in one step with
xchg().
The callback unlocks the returned buffers before it calls complete(), so
write() can finish while the completion of its last claim callback is
still pending, and that completion can then wake write_complete() before
the RECONFIG_STATUS reply. write_complete() therefore keeps waiting on
-EAGAIN and only re-sends RECONFIG_STATUS, bounded by the deadline, on
an actual BUSY reply.
As in the existing write() loop, status bits are honoured even when the
wait times out. Visible changes: write_init() now returns -EFAULT on
SVC_STATUS_ERROR and -EAGAIN for a reply without OK, where it returned
-ETIMEDOUT for both, and write_init() and write_complete() accept a
success bit that is set when their wait times out.
Also clear priv->status at the start of write_init so that bits set by a
late reply to an aborted previous write do not affect the new request.
stratix10_svc_done() is left at the call sites since it ends the whole
reconfiguration sequence rather than a single command.
Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@altera.com>
---
Notes:
Changes in v3:
- Replaces "prefer ERROR over BUFFER_DONE". Add s10_svc_wait() and
s10_svc_send_and_wait(); write_init and write_complete use
s10_svc_send_and_wait(), write() uses s10_svc_wait() after its
data/claim send (Xu Yilun).
- ERROR now also wins over COMPLETED and over OK (Xu Yilun).
- write_complete ignores a stray wake-up from the last claim callback
instead of counting it against the deadline.
drivers/fpga/stratix10-soc.c | 163 ++++++++++++++++++++---------------
1 file changed, 94 insertions(+), 69 deletions(-)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index a69f63f0236b..074a99fc3a4b 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -72,6 +72,69 @@ static int s10_svc_send_msg(struct s10_priv *priv,
return ret;
}
+/*
+ * s10_svc_wait - wait for a service layer reply and decode its status
+ * priv: private data
+ * done_bits: mask of SVC_STATUS_* bits that indicate success
+ * timeout: maximum time to wait for the reply, in jiffies
+ *
+ * Status bits accumulate in priv->status until they are consumed here, and
+ * SVC_STATUS_ERROR takes precedence over any success bit set alongside it.
+ * Returns 0 if a bit in done_bits was set, -EFAULT on SVC_STATUS_ERROR,
+ * -EBUSY on SVC_STATUS_BUSY, -ETIMEDOUT if no reply arrived, or -EAGAIN if
+ * the wake-up carried none of these bits (e.g. a late completion whose
+ * status bits were already consumed).
+ */
+static int s10_svc_wait(struct s10_priv *priv, unsigned long done_bits,
+ unsigned long timeout)
+{
+ struct device *dev = priv->client.dev;
+ unsigned long time_left, status;
+
+ time_left = wait_for_completion_timeout(&priv->status_return_completion,
+ timeout);
+ status = xchg(&priv->status, 0);
+
+ if (status & BIT(SVC_STATUS_ERROR)) {
+ dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
+ return -EFAULT;
+ }
+
+ if (status & done_bits)
+ return 0;
+
+ if (status & BIT(SVC_STATUS_BUSY))
+ return -EBUSY;
+
+ if (!time_left) {
+ dev_err(dev, "timeout waiting for service layer reply\n");
+ return -ETIMEDOUT;
+ }
+
+ return -EAGAIN;
+}
+
+/*
+ * s10_svc_send_and_wait - send a command and wait for its reply
+ * Returns a negative errno from s10_svc_send_msg(), otherwise the result of
+ * s10_svc_wait().
+ */
+static int s10_svc_send_and_wait(struct s10_priv *priv,
+ enum stratix10_svc_command_code command,
+ void *payload, u32 payload_length,
+ unsigned long done_bits, unsigned long timeout)
+{
+ int ret;
+
+ reinit_completion(&priv->status_return_completion);
+
+ ret = s10_svc_send_msg(priv, command, payload, payload_length);
+ if (ret < 0)
+ return ret;
+
+ return s10_svc_wait(priv, done_bits, timeout);
+}
+
/*
* Free buffers allocated from the service layer's pool that are not in use.
* Return true when all buffers are freed.
@@ -159,8 +222,8 @@ static void s10_receive_callback(struct stratix10_svc_client *client,
status = data->status;
/*
- * Here we set status bits as we receive them. Elsewhere, we always use
- * test_and_clear_bit() to check status in priv->status
+ * Here we set status bits as we receive them. s10_svc_wait() consumes
+ * them from priv->status.
*/
for (i = 0; i <= SVC_STATUS_ERROR; i++)
if (status & (1 << i))
@@ -190,6 +253,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
uint i;
int ret;
+ /* A reply to an aborted previous write may have left status bits. */
+ priv->status = 0;
+
ctype.flags = 0;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_dbg(dev, "Requesting partial reconfiguration.\n");
@@ -198,23 +264,11 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
dev_dbg(dev, "Requesting full reconfiguration.\n");
}
- reinit_completion(&priv->status_return_completion);
- ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
- &ctype, sizeof(ctype));
- if (ret < 0)
- goto init_error;
-
- if (!wait_for_completion_timeout(&priv->status_return_completion,
- S10_RECONFIG_TIMEOUT)) {
- dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
- ret = -ETIMEDOUT;
- goto init_error;
- }
-
- if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
- ret = -ETIMEDOUT;
+ ret = s10_svc_send_and_wait(priv, COMMAND_RECONFIG,
+ &ctype, sizeof(ctype),
+ BIT(SVC_STATUS_OK), S10_RECONFIG_TIMEOUT);
+ if (ret)
goto init_error;
- }
/* Allocate buffers from the service layer's pool. */
for (i = 0; i < NUM_SVC_BUFS; i++) {
@@ -287,7 +341,6 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
{
struct s10_priv *priv = mgr->priv;
struct device *dev = priv->client.dev;
- long wait_status;
int sent = 0;
int ret = 0;
@@ -320,30 +373,15 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
* Always wait for the completion rather than skipping it when
* priv->status is already set. The callback may have fired
* between the CLAIM/SUBMIT send and here; if so, complete()
- * has already incremented the done counter and
- * wait_for_completion_timeout() returns immediately.
+ * has already incremented the done counter and s10_svc_wait()
+ * returns immediately.
*/
- wait_status = wait_for_completion_timeout(
- &priv->status_return_completion, S10_BUFFER_TIMEOUT);
-
- if (test_and_clear_bit(SVC_STATUS_BUFFER_DONE, &priv->status) ||
- test_and_clear_bit(SVC_STATUS_BUFFER_SUBMITTED,
- &priv->status)) {
- ret = 0;
- continue;
- }
-
- if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
- dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
- ret = -EFAULT;
- break;
- }
-
- if (!wait_status) {
- dev_err(dev, "timeout waiting for svc layer buffers\n");
- ret = -ETIMEDOUT;
+ ret = s10_svc_wait(priv, BIT(SVC_STATUS_BUFFER_SUBMITTED) |
+ BIT(SVC_STATUS_BUFFER_DONE),
+ S10_BUFFER_TIMEOUT);
+ if (ret == -EFAULT || ret == -ETIMEDOUT)
break;
- }
+ ret = 0;
}
if (!s10_free_buffers(mgr))
@@ -366,35 +404,22 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
deadline = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
do {
- reinit_completion(&priv->status_return_completion);
-
- ret = s10_svc_send_msg(priv, COMMAND_RECONFIG_STATUS, NULL, 0);
- if (ret < 0)
- break;
-
- if (!wait_for_completion_timeout(&priv->status_return_completion,
- S10_RECONFIG_STATUS_TIMEOUT)) {
- dev_err(dev, "timeout waiting for RECONFIG_STATUS\n");
- ret = -ETIMEDOUT;
- break;
- }
-
- if (test_and_clear_bit(SVC_STATUS_COMPLETED, &priv->status))
- break;
-
- if (test_and_clear_bit(SVC_STATUS_ERROR, &priv->status)) {
- dev_err(dev, "ERROR - giving up - SVC_STATUS_ERROR\n");
- ret = -EFAULT;
- break;
- }
+ ret = s10_svc_send_and_wait(priv, COMMAND_RECONFIG_STATUS,
+ NULL, 0, BIT(SVC_STATUS_COMPLETED),
+ S10_RECONFIG_STATUS_TIMEOUT);
+ /*
+ * A late completion from the last RECONFIG_DATA_CLAIM callback
+ * can wake us before the reply to this request arrives.
+ */
+ while (ret == -EAGAIN)
+ ret = s10_svc_wait(priv, BIT(SVC_STATUS_COMPLETED),
+ S10_RECONFIG_STATUS_TIMEOUT);
+ } while (ret == -EBUSY && time_before(jiffies, deadline));
- if (time_after_eq(jiffies, deadline)) {
- dev_err(dev,
- "timeout waiting for RECONFIG_COMPLETED\n");
- ret = -ETIMEDOUT;
- break;
- }
- } while (1);
+ if (ret == -EBUSY) {
+ dev_err(dev, "timeout waiting for RECONFIG_COMPLETED\n");
+ ret = -ETIMEDOUT;
+ }
stratix10_svc_done(priv->chan);
--
2.49.GIT
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-20 7:49 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-22 16:02 [PATCH 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
2026-09-22 16:02 ` [PATCH 2/3] fpga: stratix10-soc: prefer ERROR over BUFFER_DONE Adrian Ng Ho Yin
2026-09-28 11:59 ` Xu Yilun
2026-09-22 16:02 ` [PATCH 3/3] fpga: stratix10-soc: floor config_complete_timeout_us Adrian Ng Ho Yin
2026-09-28 12:14 ` Xu Yilun
2026-10-20 7:49 ` [PATCH v3 0/3] fpga: stratix10-soc: harden wait, ERROR order, and timeout Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 1/3] fpga: stratix10-soc: always wait for svc buffer completion Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 2/3] fpga: stratix10-soc: use config_complete_timeout_us as a deadline Adrian Ng Ho Yin
2026-10-20 7:49 ` [PATCH v3 3/3] fpga: stratix10-soc: add SVC wait helper and prefer ERROR Adrian Ng Ho Yin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®