From: Udipto Goswami <quic_ugoswami@quicinc.com>
To: Thinh Nguyen <Thinh.Nguyen@synopsys.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Pratham Pratap <quic_ppratap@quicinc.com>,
Jack Pham <quic_jackp@quicinc.com>,
Johan Hovold <johan+linaro@kernel.org>,
"Oliver Neukum" <oneukum@suse.com>, <linux-usb@vger.kernel.org>,
Udipto Goswami <quic_ugoswami@quicinc.com>
Subject: [PATCH v7] usb: dwc3: debugfs: Prevent any register access when devices is runtime suspended
Date: Thu, 27 Apr 2023 14:40:56 +0530 [thread overview]
Message-ID: <20230427091056.18716-1-quic_ugoswami@quicinc.com> (raw)
When the dwc3 device is runtime suspended, various required clocks would
get disabled and it is not guaranteed that access to any registers would
work. Depending on the SoC glue, a register read could be as benign as
returning 0 or be fatal enough to hang the system.
In order to prevent such scenarios of fatal errors, make sure to resume
dwc3 then allow the function to proceed.
Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
---
v7: Replaced pm_runtime_put with pm_runtime_put_sync & returned proper values.
v6: Added changes to handle get_dync failure appropriately.
v5: Reworked the patch to resume dwc3 while accessing the registers.
v4: Introduced pm_runtime_get_if_in_use in order to make sure dwc3 isn't
suspended while accessing the registers.
v3: Replace pr_err to dev_err.
v2: Replaced return 0 with -EINVAL & seq_puts with pr_err.
drivers/usb/dwc3/debugfs.c | 128 ++++++++++++++++++++++++++++++++++++-
1 file changed, 126 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index e4a2560b9dc0..859184de37b6 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -332,6 +332,13 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
unsigned int current_mode;
unsigned long flags;
u32 reg;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
@@ -349,6 +356,7 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
break;
}
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -395,6 +403,13 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 reg;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -414,6 +429,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
}
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -463,6 +479,13 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 reg;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -493,6 +516,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
seq_printf(s, "UNKNOWN %d\n", reg);
}
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -509,6 +533,7 @@ static ssize_t dwc3_testmode_write(struct file *file,
unsigned long flags;
u32 testmode = 0;
char buf[32];
+ int ret;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -526,10 +551,17 @@ static ssize_t dwc3_testmode_write(struct file *file,
else
testmode = 0;
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_set_test_mode(dwc, testmode);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return count;
}
@@ -548,12 +580,20 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
enum dwc3_link_state state;
u32 reg;
u8 speed;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
seq_puts(s, "Not available\n");
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -566,6 +606,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
dwc3_gadget_hs_link_string(state));
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -584,6 +625,7 @@ static ssize_t dwc3_link_state_write(struct file *file,
char buf[32];
u32 reg;
u8 speed;
+ int ret;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -603,11 +645,18 @@ static ssize_t dwc3_link_state_write(struct file *file,
else
return -EINVAL;
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
spin_unlock_irqrestore(&dwc->lock, flags);
- return -EINVAL;
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
}
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -616,12 +665,14 @@ static ssize_t dwc3_link_state_write(struct file *file,
if (speed < DWC3_DSTS_SUPERSPEED &&
state != DWC3_LINK_STATE_RECOV) {
spin_unlock_irqrestore(&dwc->lock, flags);
- return -EINVAL;
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
}
dwc3_gadget_set_link_state(dwc, state);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return count;
}
@@ -645,6 +696,13 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
unsigned long flags;
u32 mdwidth;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
@@ -657,6 +715,7 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -667,6 +726,13 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
unsigned long flags;
u32 mdwidth;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
@@ -679,6 +745,7 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -688,12 +755,20 @@ static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -703,12 +778,20 @@ static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -718,12 +801,20 @@ static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -733,12 +824,20 @@ static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -748,12 +847,20 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
u32 val;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -798,6 +905,13 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int i;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
if (dep->number <= 1) {
@@ -827,6 +941,7 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
out:
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -839,6 +954,13 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
u32 lower_32_bits;
u32 upper_32_bits;
u32 reg;
+ int ret;
+
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dwc->dev);
+ return ret;
+ }
spin_lock_irqsave(&dwc->lock, flags);
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
@@ -851,6 +973,7 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
seq_printf(s, "0x%016llx\n", ep_info);
spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_put_sync(dwc->dev);
return 0;
}
@@ -910,6 +1033,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
dwc->regset->regs = dwc3_regs;
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
+ dwc->regset->dev = dwc->dev;
root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
dwc->debug_root = root;
--
2.17.1
next reply other threads:[~2023-04-27 9:11 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-27 9:10 Udipto Goswami [this message]
2023-04-27 14:26 ` [PATCH v7] usb: dwc3: debugfs: Prevent any register access when devices is runtime suspended Greg Kroah-Hartman
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=20230427091056.18716-1-quic_ugoswami@quicinc.com \
--to=quic_ugoswami@quicinc.com \
--cc=Thinh.Nguyen@synopsys.com \
--cc=gregkh@linuxfoundation.org \
--cc=johan+linaro@kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=oneukum@suse.com \
--cc=quic_jackp@quicinc.com \
--cc=quic_ppratap@quicinc.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox