From b40d293f3767f196c3fc926125c467033981a02a Mon Sep 17 00:00:00 2001 From: Jett Rink Date: Fri, 7 Jun 2019 16:18:56 -0600 Subject: USB PD: disable port if TCPC interrupt storm detected If we get too many interrupts too fast, then we will starve the rest of the EC. Disable an overactive port for 5 seconds. BRANCH=none BUG=b:134702480,b:128701054 TEST=7-magic hub no longer watch dog resets phaser port 0, HDMI dongle plugged into port 1 still able to function while port 0 is periodically suspending Change-Id: Ic2d13ecc64990994ffc8e3fb68537aa909657745 Signed-off-by: Jett Rink Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1650484 Tested-by: Diana Z Reviewed-by: Edward Hill Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1693158 Commit-Queue: Zhuohao Lee Tested-by: Zhuohao Lee Reviewed-by: Daisuke Nojiri --- common/usb_pd_protocol.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'common/usb_pd_protocol.c') diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 3c72d9b0da..a75baa5c14 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -2557,6 +2557,14 @@ void schedule_deferred_pd_interrupt(const int port) task_set_event(pd_int_task_id[port], PD_PROCESS_INTERRUPT, 0); } +/* + * Theoretically, we may need to support up to 480 USB-PD packets per second for + * intensive operations such as FW update over PD. This value has tested well + * preventing watchdog resets with a single bad port partner plugged in. + */ +#define ALERT_STORM_MAX_COUNT 480 +#define ALERT_STORM_INTERVAL SECOND + /** * Main task entry point that handles PD interrupts for a single port * @@ -2567,6 +2575,10 @@ void pd_interrupt_handler_task(void *p) { const int port = (int) p; const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port); + struct { + int count; + uint32_t time; + } storm_tracker[CONFIG_USB_PD_PORT_COUNT] = { 0 }; ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT); @@ -2588,8 +2600,30 @@ void pd_interrupt_handler_task(void *p) * PD_PROCESS_INTERRUPT to check if we missed anything. */ while ((tcpc_get_alert_status() & port_mask) && - pd_is_port_enabled(port)) + pd_is_port_enabled(port)) { + uint32_t now; + tcpc_alert(port); + + now = get_time().le.lo; + if (time_after(now, storm_tracker[port].time)) { + storm_tracker[port].time = + now + ALERT_STORM_INTERVAL; + /* + * Start at 1 since we are processing + * an interrupt now + */ + storm_tracker[port].count = 1; + } else if (++storm_tracker[port].count > + ALERT_STORM_MAX_COUNT) { + CPRINTS("C%d Interrupt storm detected. " + "Disabling port for 5 seconds.", + port); + + pd_set_suspend(port, 1); + pd_deferred_resume(port); + } + } } } } @@ -3492,6 +3526,11 @@ void pd_task(void *u) CPRINTS("TCPC p%d restart failed!", port); break; } + /* Set the CC termination and state back to default */ + tcpm_set_cc(port, + PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE ? + TYPEC_CC_RP : + TYPEC_CC_RD); set_state(port, PD_DEFAULT_STATE(port)); CPRINTS("TCPC p%d resumed!", port); #endif @@ -4379,7 +4418,7 @@ DECLARE_DEFERRED(resume_pd_port); void pd_deferred_resume(int port) { atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, SECOND); + hook_call_deferred(&resume_pd_port_data, 5 * SECOND); } #endif /* CONFIG_USB_PD_DEFERRED_RESUME */ -- cgit v1.2.1