From: Javi Merino <javi.merino@arm.com>
To: "linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>
Cc: Javi Merino <Javi.Merino@arm.com>,
"jassisinghbrar@gmail.com" <jassisinghbrar@gmail.com>,
"kgene.kim@samsung.com" <kgene.kim@samsung.com>,
"boojin.kim@samsung.com" <boojin.kim@samsung.com>,
"linux-samsung-soc@vger.kernel.org"
<linux-samsung-soc@vger.kernel.org>,
Jassi Brar <jaswinder.singh@linaro.org>
Subject: Re: [PATCH] ARM: PL330: Fix driver freeze
Date: Thu, 15 Dec 2011 17:48:48 +0000 [thread overview]
Message-ID: <4EEA3300.9000402@arm.com> (raw)
In-Reply-To: <1323631637-9610-1-git-send-email-javi.merino@arm.com>
On 11/12/11 19:27, Javi Merino wrote:
> Add a req_running field to the pl330_thread to track which request (if
> any) has been submitted to the DMA. This mechanism replaces the old
> one in which we tried to guess the same by looking at the PC of the
> DMA, which could prevent the driver from sending more requests if it
> didn't guess correctly.
>
> Signed-off-by: Javi Merino <javi.merino@arm.com>
> Cc: Jassi Brar <jaswinder.singh@linaro.org>
> ---
> arch/arm/common/pl330.c | 116 ++++++++++++++++++++---------------------------
> 1 files changed, 49 insertions(+), 67 deletions(-)
>
> diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c
> index f407a6b..8d8df74 100644
> --- a/arch/arm/common/pl330.c
> +++ b/arch/arm/common/pl330.c
> @@ -221,17 +221,6 @@
> */
> #define MCODE_BUFF_PER_REQ 256
>
> -/*
> - * Mark a _pl330_req as free.
> - * We do it by writing DMAEND as the first instruction
> - * because no valid request is going to have DMAEND as
> - * its first instruction to execute.
> - */
> -#define MARK_FREE(req) do { \
> - _emit_END(0, (req)->mc_cpu); \
> - (req)->mc_len = 0; \
> - } while (0)
> -
> /* If the _pl330_req is available to the client */
> #define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)
>
> @@ -301,8 +290,10 @@ struct pl330_thread {
> struct pl330_dmac *dmac;
> /* Only two at a time */
> struct _pl330_req req[2];
> - /* Index of the last submitted request */
> + /* Index of the last enqueued request */
> unsigned lstenq;
> + /* Index of the last submitted request or -1 if the DMA is stopped */
> + int req_running;
> };
>
> enum pl330_dmac_state {
> @@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
> writel(0, regs + DBGCMD);
> }
>
> +/*
> + * Mark a _pl330_req as free.
> + * We do it by writing DMAEND as the first instruction
> + * because no valid request is going to have DMAEND as
> + * its first instruction to execute.
> + */
> +static void mark_free(struct pl330_thread *thrd, int idx)
> +{
> + struct _pl330_req *req = &thrd->req[idx];
> +
> + _emit_END(0, req->mc_cpu);
> + req->mc_len = 0;
> +
> + thrd->req_running = -1;
> +}
> +
> static inline u32 _state(struct pl330_thread *thrd)
> {
> void __iomem *regs = thrd->dmac->pinfo->base;
> @@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd)
> }
> }
>
> -/* If the request 'req' of thread 'thrd' is currently active */
> -static inline bool _req_active(struct pl330_thread *thrd,
> - struct _pl330_req *req)
> -{
> - void __iomem *regs = thrd->dmac->pinfo->base;
> - u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id));
> -
> - if (IS_FREE(req))
> - return false;
> -
> - return (pc >= buf && pc <= buf + req->mc_len) ? true : false;
> -}
> -
> -/* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */
> -static inline unsigned _thrd_active(struct pl330_thread *thrd)
> -{
> - if (_req_active(thrd, &thrd->req[0]))
> - return 1; /* First req active */
> -
> - if (_req_active(thrd, &thrd->req[1]))
> - return 2; /* Second req active */
> -
> - return 0;
> -}
> -
> static void _stop(struct pl330_thread *thrd)
> {
> void __iomem *regs = thrd->dmac->pinfo->base;
> @@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd)
> struct _arg_GO go;
> unsigned ns;
> u8 insn[6] = {0, 0, 0, 0, 0, 0};
> + int idx;
>
> /* Return if already ACTIVE */
> if (_state(thrd) != PL330_STATE_STOPPED)
> return true;
>
> - if (!IS_FREE(&thrd->req[1 - thrd->lstenq]))
> - req = &thrd->req[1 - thrd->lstenq];
> - else if (!IS_FREE(&thrd->req[thrd->lstenq]))
> - req = &thrd->req[thrd->lstenq];
> - else
> - req = NULL;
> + idx = 1 - thrd->lstenq;
> + if (!IS_FREE(&thrd->req[idx]))
> + req = &thrd->req[idx];
> + else {
> + idx = thrd->lstenq;
> + if (!IS_FREE(&thrd->req[idx]))
> + req = &thrd->req[idx];
> + else
> + req = NULL;
> + }
>
> /* Return if no request */
> if (!req || !req->r)
> @@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd)
> /* Only manager can execute GO */
> _execute_DBGINSN(thrd, insn, true);
>
> + thrd->req_running = idx;
> +
> return true;
> }
>
> @@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data)
>
> thrd->req[0].r = NULL;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 0);
> + mark_free(thrd, 1);
>
> /* Clear the reset flag */
> pl330->dmac_tbd.reset_chan &= ~(1 << i);
> @@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi)
>
> thrd = &pl330->channels[id];
>
> - active = _thrd_active(thrd);
> - if (!active) /* Aborted */
> + active = thrd->req_running;
> + if (active == -1) /* Aborted */
> continue;
>
> - active -= 1;
> -
> rqdone = &thrd->req[active];
> - MARK_FREE(rqdone);
> + mark_free(thrd, active);
>
> /* Get going again ASAP */
> _start(thrd);
> @@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
> struct pl330_thread *thrd = ch_id;
> struct pl330_dmac *pl330;
> unsigned long flags;
> - int ret = 0, active;
> + int ret = 0, active = thrd->req_running;
>
> if (!thrd || thrd->free || thrd->dmac->state == DYING)
> return -EINVAL;
> @@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
>
> thrd->req[0].r = NULL;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 0);
> + mark_free(thrd, 1);
> break;
>
> case PL330_OP_ABORT:
> - active = _thrd_active(thrd);
> -
> /* Make sure the channel is stopped */
> _stop(thrd);
>
> /* ABORT is only for the active req */
> - if (!active)
> + if (active == -1)
> break;
>
> - active--;
> -
> thrd->req[active].r = NULL;
> - MARK_FREE(&thrd->req[active]);
> + mark_free(thrd, active);
>
> /* Start the next */
> case PL330_OP_START:
> - if (!_thrd_active(thrd) && !_start(thrd))
> + if ((active == -1) && !_start(thrd))
> ret = -EIO;
> break;
>
> @@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus)
> else
> pstatus->faulting = false;
>
> - active = _thrd_active(thrd);
> + active = thrd->req_running;
>
> - if (!active) {
> + if (active == -1) {
> /* Indicate that the thread is not running */
> pstatus->top_req = NULL;
> pstatus->wait_req = NULL;
> } else {
> - active--;
> pstatus->top_req = thrd->req[active].r;
> pstatus->wait_req = !IS_FREE(&thrd->req[1 - active])
> ? thrd->req[1 - active].r : NULL;
> @@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi)
> thrd->free = false;
> thrd->lstenq = 1;
> thrd->req[0].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> + mark_free(thrd, 0);
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 1);
> break;
> }
> }
> @@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd)
> thrd->req[0].mc_bus = pl330->mcode_bus
> + (thrd->id * pi->mcbufsz);
> thrd->req[0].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> + mark_free(thrd, 0);
>
> thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
> + pi->mcbufsz / 2;
> thrd->req[1].mc_bus = thrd->req[0].mc_bus
> + pi->mcbufsz / 2;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 1);
> }
>
> static int dmac_alloc_threads(struct pl330_dmac *pl330)
I've done some testing and I haven't been able to break it. Can
somebody please test this patch with the workload that failed (audio in
exynos I believe)?
Thanks,
Javi
WARNING: multiple messages have this Message-ID (diff)
From: javi.merino@arm.com (Javi Merino)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] ARM: PL330: Fix driver freeze
Date: Thu, 15 Dec 2011 17:48:48 +0000 [thread overview]
Message-ID: <4EEA3300.9000402@arm.com> (raw)
In-Reply-To: <1323631637-9610-1-git-send-email-javi.merino@arm.com>
On 11/12/11 19:27, Javi Merino wrote:
> Add a req_running field to the pl330_thread to track which request (if
> any) has been submitted to the DMA. This mechanism replaces the old
> one in which we tried to guess the same by looking at the PC of the
> DMA, which could prevent the driver from sending more requests if it
> didn't guess correctly.
>
> Signed-off-by: Javi Merino <javi.merino@arm.com>
> Cc: Jassi Brar <jaswinder.singh@linaro.org>
> ---
> arch/arm/common/pl330.c | 116 ++++++++++++++++++++---------------------------
> 1 files changed, 49 insertions(+), 67 deletions(-)
>
> diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c
> index f407a6b..8d8df74 100644
> --- a/arch/arm/common/pl330.c
> +++ b/arch/arm/common/pl330.c
> @@ -221,17 +221,6 @@
> */
> #define MCODE_BUFF_PER_REQ 256
>
> -/*
> - * Mark a _pl330_req as free.
> - * We do it by writing DMAEND as the first instruction
> - * because no valid request is going to have DMAEND as
> - * its first instruction to execute.
> - */
> -#define MARK_FREE(req) do { \
> - _emit_END(0, (req)->mc_cpu); \
> - (req)->mc_len = 0; \
> - } while (0)
> -
> /* If the _pl330_req is available to the client */
> #define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)
>
> @@ -301,8 +290,10 @@ struct pl330_thread {
> struct pl330_dmac *dmac;
> /* Only two at a time */
> struct _pl330_req req[2];
> - /* Index of the last submitted request */
> + /* Index of the last enqueued request */
> unsigned lstenq;
> + /* Index of the last submitted request or -1 if the DMA is stopped */
> + int req_running;
> };
>
> enum pl330_dmac_state {
> @@ -778,6 +769,22 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
> writel(0, regs + DBGCMD);
> }
>
> +/*
> + * Mark a _pl330_req as free.
> + * We do it by writing DMAEND as the first instruction
> + * because no valid request is going to have DMAEND as
> + * its first instruction to execute.
> + */
> +static void mark_free(struct pl330_thread *thrd, int idx)
> +{
> + struct _pl330_req *req = &thrd->req[idx];
> +
> + _emit_END(0, req->mc_cpu);
> + req->mc_len = 0;
> +
> + thrd->req_running = -1;
> +}
> +
> static inline u32 _state(struct pl330_thread *thrd)
> {
> void __iomem *regs = thrd->dmac->pinfo->base;
> @@ -836,31 +843,6 @@ static inline u32 _state(struct pl330_thread *thrd)
> }
> }
>
> -/* If the request 'req' of thread 'thrd' is currently active */
> -static inline bool _req_active(struct pl330_thread *thrd,
> - struct _pl330_req *req)
> -{
> - void __iomem *regs = thrd->dmac->pinfo->base;
> - u32 buf = req->mc_bus, pc = readl(regs + CPC(thrd->id));
> -
> - if (IS_FREE(req))
> - return false;
> -
> - return (pc >= buf && pc <= buf + req->mc_len) ? true : false;
> -}
> -
> -/* Returns 0 if the thread is inactive, ID of active req + 1 otherwise */
> -static inline unsigned _thrd_active(struct pl330_thread *thrd)
> -{
> - if (_req_active(thrd, &thrd->req[0]))
> - return 1; /* First req active */
> -
> - if (_req_active(thrd, &thrd->req[1]))
> - return 2; /* Second req active */
> -
> - return 0;
> -}
> -
> static void _stop(struct pl330_thread *thrd)
> {
> void __iomem *regs = thrd->dmac->pinfo->base;
> @@ -892,17 +874,22 @@ static bool _trigger(struct pl330_thread *thrd)
> struct _arg_GO go;
> unsigned ns;
> u8 insn[6] = {0, 0, 0, 0, 0, 0};
> + int idx;
>
> /* Return if already ACTIVE */
> if (_state(thrd) != PL330_STATE_STOPPED)
> return true;
>
> - if (!IS_FREE(&thrd->req[1 - thrd->lstenq]))
> - req = &thrd->req[1 - thrd->lstenq];
> - else if (!IS_FREE(&thrd->req[thrd->lstenq]))
> - req = &thrd->req[thrd->lstenq];
> - else
> - req = NULL;
> + idx = 1 - thrd->lstenq;
> + if (!IS_FREE(&thrd->req[idx]))
> + req = &thrd->req[idx];
> + else {
> + idx = thrd->lstenq;
> + if (!IS_FREE(&thrd->req[idx]))
> + req = &thrd->req[idx];
> + else
> + req = NULL;
> + }
>
> /* Return if no request */
> if (!req || !req->r)
> @@ -933,6 +920,8 @@ static bool _trigger(struct pl330_thread *thrd)
> /* Only manager can execute GO */
> _execute_DBGINSN(thrd, insn, true);
>
> + thrd->req_running = idx;
> +
> return true;
> }
>
> @@ -1382,8 +1371,8 @@ static void pl330_dotask(unsigned long data)
>
> thrd->req[0].r = NULL;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 0);
> + mark_free(thrd, 1);
>
> /* Clear the reset flag */
> pl330->dmac_tbd.reset_chan &= ~(1 << i);
> @@ -1461,14 +1450,12 @@ int pl330_update(const struct pl330_info *pi)
>
> thrd = &pl330->channels[id];
>
> - active = _thrd_active(thrd);
> - if (!active) /* Aborted */
> + active = thrd->req_running;
> + if (active == -1) /* Aborted */
> continue;
>
> - active -= 1;
> -
> rqdone = &thrd->req[active];
> - MARK_FREE(rqdone);
> + mark_free(thrd, active);
>
> /* Get going again ASAP */
> _start(thrd);
> @@ -1509,7 +1496,7 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
> struct pl330_thread *thrd = ch_id;
> struct pl330_dmac *pl330;
> unsigned long flags;
> - int ret = 0, active;
> + int ret = 0, active = thrd->req_running;
>
> if (!thrd || thrd->free || thrd->dmac->state == DYING)
> return -EINVAL;
> @@ -1525,28 +1512,24 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
>
> thrd->req[0].r = NULL;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 0);
> + mark_free(thrd, 1);
> break;
>
> case PL330_OP_ABORT:
> - active = _thrd_active(thrd);
> -
> /* Make sure the channel is stopped */
> _stop(thrd);
>
> /* ABORT is only for the active req */
> - if (!active)
> + if (active == -1)
> break;
>
> - active--;
> -
> thrd->req[active].r = NULL;
> - MARK_FREE(&thrd->req[active]);
> + mark_free(thrd, active);
>
> /* Start the next */
> case PL330_OP_START:
> - if (!_thrd_active(thrd) && !_start(thrd))
> + if ((active == -1) && !_start(thrd))
> ret = -EIO;
> break;
>
> @@ -1587,14 +1570,13 @@ int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus)
> else
> pstatus->faulting = false;
>
> - active = _thrd_active(thrd);
> + active = thrd->req_running;
>
> - if (!active) {
> + if (active == -1) {
> /* Indicate that the thread is not running */
> pstatus->top_req = NULL;
> pstatus->wait_req = NULL;
> } else {
> - active--;
> pstatus->top_req = thrd->req[active].r;
> pstatus->wait_req = !IS_FREE(&thrd->req[1 - active])
> ? thrd->req[1 - active].r : NULL;
> @@ -1659,9 +1641,9 @@ void *pl330_request_channel(const struct pl330_info *pi)
> thrd->free = false;
> thrd->lstenq = 1;
> thrd->req[0].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> + mark_free(thrd, 0);
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 1);
> break;
> }
> }
> @@ -1767,14 +1749,14 @@ static inline void _reset_thread(struct pl330_thread *thrd)
> thrd->req[0].mc_bus = pl330->mcode_bus
> + (thrd->id * pi->mcbufsz);
> thrd->req[0].r = NULL;
> - MARK_FREE(&thrd->req[0]);
> + mark_free(thrd, 0);
>
> thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
> + pi->mcbufsz / 2;
> thrd->req[1].mc_bus = thrd->req[0].mc_bus
> + pi->mcbufsz / 2;
> thrd->req[1].r = NULL;
> - MARK_FREE(&thrd->req[1]);
> + mark_free(thrd, 1);
> }
>
> static int dmac_alloc_threads(struct pl330_dmac *pl330)
I've done some testing and I haven't been able to break it. Can
somebody please test this patch with the workload that failed (audio in
exynos I believe)?
Thanks,
Javi
next prev parent reply other threads:[~2011-12-15 17:48 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-19 17:11 [PATCH] ARM: pl330: Fix a race condition Javi Merino
2011-09-19 18:07 ` Jassi Brar
2011-09-20 13:36 ` Javi Merino
2011-10-05 12:57 ` Javi Merino
2011-10-06 9:10 ` [PATCH v2] " Javi Merino
2011-11-05 19:05 ` Thomas Abraham
2011-11-05 19:05 ` Thomas Abraham
2011-11-07 10:48 ` Javi Merino
2011-11-07 10:48 ` Javi Merino
2011-11-07 11:03 ` Thomas Abraham
2011-11-07 11:03 ` Thomas Abraham
2011-11-28 8:23 ` Boojin Kim
2011-11-28 8:23 ` Boojin Kim
2011-11-28 16:36 ` Javi Merino
2011-11-28 16:36 ` Javi Merino
2011-11-29 3:41 ` Boojin Kim
2011-11-29 3:41 ` Boojin Kim
2011-11-29 9:53 ` Javi Merino
2011-11-29 9:53 ` Javi Merino
2011-11-29 10:37 ` Jassi Brar
2011-11-29 10:37 ` Jassi Brar
2011-12-07 7:52 ` Kukjin Kim
2011-12-07 7:52 ` Kukjin Kim
2011-12-07 10:01 ` Javi Merino
2011-12-07 10:01 ` Javi Merino
2011-12-07 20:54 ` Javi Merino
2011-12-07 20:54 ` Javi Merino
2011-12-09 11:58 ` Javi Merino
2011-12-09 11:58 ` Javi Merino
2011-12-09 13:04 ` Jassi Brar
2011-12-09 13:04 ` Jassi Brar
2011-12-09 13:41 ` Javi Merino
2011-12-09 13:41 ` Javi Merino
2011-12-09 14:15 ` Jassi Brar
2011-12-09 14:15 ` Jassi Brar
2011-12-09 14:52 ` Javi Merino
2011-12-09 14:52 ` Javi Merino
2011-12-09 16:50 ` Jassi Brar
2011-12-09 16:50 ` Jassi Brar
2011-12-09 19:50 ` Javi Merino
2011-12-09 19:50 ` Javi Merino
2011-12-11 10:51 ` Jassi Brar
2011-12-11 10:51 ` Jassi Brar
2011-12-11 15:09 ` Javi Merino
2011-12-11 15:09 ` Javi Merino
2011-12-11 17:10 ` Jassi Brar
2011-12-11 17:10 ` Jassi Brar
2011-12-11 17:42 ` Javi Merino
2011-12-11 17:42 ` Javi Merino
2011-12-11 19:27 ` [PATCH] ARM: PL330: Fix driver freeze Javi Merino
2011-12-11 19:27 ` Javi Merino
2011-12-15 17:48 ` Javi Merino [this message]
2011-12-15 17:48 ` Javi Merino
2011-12-16 9:01 ` Tushar Behera
2011-12-16 9:01 ` Tushar Behera
2011-12-16 6:27 ` Jassi Brar
2011-12-16 6:27 ` Jassi Brar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4EEA3300.9000402@arm.com \
--to=javi.merino@arm.com \
--cc=boojin.kim@samsung.com \
--cc=jassisinghbrar@gmail.com \
--cc=jaswinder.singh@linaro.org \
--cc=kgene.kim@samsung.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-samsung-soc@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.