[PATCH v4] usb: dwc3: debugfs: Prevent any register access when devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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, bail out of debugfs
function is dwc3 is suspended.

Signed-off-by: Oliver Neukum <oneukum@xxxxxxxx>
Signed-off-by: Udipto Goswami <quic_ugoswami@xxxxxxxxxxx>
---
v4: Introduced pm_runtime_get_if_in_use in order to make sure dwc3 isn't
suspended while accessing the registers.

 drivers/usb/dwc3/debugfs.c | 191 ++++++++++++++++++++++++++++++++-----
 1 file changed, 169 insertions(+), 22 deletions(-)

diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 850df0e6bcab..e57cafb7da4b 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -543,13 +543,25 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
 	enum dwc3_link_state	state;
 	u32			reg;
 	u8			speed;
+	int			ret = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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);
-		return 0;
+		goto err;
 	}
 
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -559,9 +571,11 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
 	seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ?
 		   dwc3_gadget_link_string(state) :
 		   dwc3_gadget_hs_link_string(state));
+err:
 	spin_unlock_irqrestore(&dwc->lock, flags);
-
-	return 0;
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 static int dwc3_link_state_open(struct inode *inode, struct file *file)
@@ -579,6 +593,20 @@ static ssize_t dwc3_link_state_write(struct file *file,
 	char			buf[32];
 	u32			reg;
 	u8			speed;
+	int			ret = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
+
 
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
@@ -601,8 +629,8 @@ static ssize_t dwc3_link_state_write(struct file *file,
 	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;
+		count = -EINVAL;
+		goto err;
 	}
 
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -610,13 +638,15 @@ 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;
+		count = -EINVAL;
+		goto err;
 	}
 
 	dwc3_gadget_set_link_state(dwc, state);
+err:
 	spin_unlock_irqrestore(&dwc->lock, flags);
-
+	pm_runtime_put(dwc->dev);
+err_nolock:
 	return count;
 }
 
@@ -640,6 +670,19 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
 	unsigned long		flags;
 	u32			mdwidth;
 	u32			val;
+	int			ret = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
@@ -650,9 +693,11 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
 	val *= mdwidth;
 	val >>= 3;
 	seq_printf(s, "%u\n", val);
-	spin_unlock_irqrestore(&dwc->lock, flags);
 
-	return 0;
+	spin_unlock_irqrestore(&dwc->lock, flags);
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
@@ -662,6 +707,19 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
 	unsigned long		flags;
 	u32			mdwidth;
 	u32			val;
+	int			ret = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
@@ -673,8 +731,9 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
 	val >>= 3;
 	seq_printf(s, "%u\n", val);
 	spin_unlock_irqrestore(&dwc->lock, flags);
-
-	return 0;
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused)
@@ -683,13 +742,27 @@ 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 = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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);
-
-	return 0;
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused)
@@ -698,13 +771,27 @@ 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 = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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);
-
-	return 0;
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused)
@@ -713,13 +800,28 @@ 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 = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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(dwc->dev);
+err_nolock:
+	return ret;
 
-	return 0;
 }
 
 static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused)
@@ -728,13 +830,29 @@ 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 = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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(dwc->dev);
+err_nolock:
+	return ret;
+
 
-	return 0;
 }
 
 static int dwc3_event_queue_show(struct seq_file *s, void *unused)
@@ -743,13 +861,28 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
 	struct dwc3		*dwc = dep->dwc;
 	unsigned long		flags;
 	u32			val;
+	int			ret = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	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(dwc->dev);
+err_nolock:
+	return ret;
 
-	return 0;
 }
 
 static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
@@ -834,6 +967,19 @@ 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 = 0;
+
+	ret = pm_runtime_get_if_in_use(dwc->dev);
+	/* check if pm runtime get fails, bail out early */
+	if (ret < 0)
+		goto err_nolock;
+
+	if (!ret) {
+		ret = -EINVAL;
+		dev_err(dwc->dev,
+				"Invalid operation, DWC3 suspended!");
+		goto err_nolock;
+	}
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
@@ -845,8 +991,9 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
 	ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
 	seq_printf(s, "0x%016llx\n", ep_info);
 	spin_unlock_irqrestore(&dwc->lock, flags);
-
-	return 0;
+	pm_runtime_put(dwc->dev);
+err_nolock:
+	return ret;
 }
 
 DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size);
-- 
2.17.1




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux