usbc: fix storm tracker overflow issue

If there is no USB-C interrupt activity for 2^31 microseconds, then
there are more than ALERT_STORM_MAX_COUNT events within 2^31
microsecond (instead of ALERT_STORM_INTERVAL), then the interrupt
storm would incorrectly detect a storm and disable the port due
to incorrect math regarding 32-bit overflow.

BRANCH=octopus and all branches with original storm detection
(CL:1650484)
BUG=b:144369187
TEST=unit test in CL

Change-Id: I90b888ac092f81d151538d6018771fb32f8e9c39
Signed-off-by: Jett Rink <jettrink@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1925668
Commit-Queue: Denis Brockus <dbrockus@chromium.org>
Reviewed-by: Denis Brockus <dbrockus@chromium.org>
This commit is contained in:
Jett Rink 2019-11-19 09:50:46 -07:00 committed by Commit Bot
parent 33991367ca
commit 044f155841
6 changed files with 145 additions and 6 deletions

View File

@ -2744,7 +2744,7 @@ void pd_interrupt_handler_task(void *p)
const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port);
struct {
int count;
uint32_t time;
timestamp_t time;
} storm_tracker[CONFIG_USB_PD_PORT_MAX_COUNT] = {};
ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT);
@ -2768,14 +2768,17 @@ void pd_interrupt_handler_task(void *p)
*/
while ((tcpc_get_alert_status() & port_mask) &&
pd_is_port_enabled(port)) {
uint32_t now;
timestamp_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;
now = get_time();
if (timestamp_expired(
storm_tracker[port].time, &now)) {
/* Reset timer into future */
storm_tracker[port].time.val =
now.val + ALERT_STORM_INTERVAL;
/*
* Start at 1 since we are processing
* an interrupt now

View File

@ -69,6 +69,7 @@ test-list-host += thermal
test-list-host += timer_dos
test-list-host += uptime
test-list-host += usb_common
test-list-host += usb_pd_int
test-list-host += usb_pd
test-list-host += usb_pd_giveback
test-list-host += usb_pd_rev30
@ -146,6 +147,7 @@ timer_calib-y=timer_calib.o
timer_dos-y=timer_dos.o
uptime-y=uptime.o
usb_common-y=usb_common_test.o
usb_pd_int-y=usb_pd_int.o
usb_pd-y=usb_pd.o
usb_pd_giveback-y=usb_pd.o
usb_pd_rev30-y=usb_pd.o

View File

@ -351,6 +351,16 @@ int ncp15wb_calculate_temp(uint16_t adc);
#undef CONFIG_USB_PE_SM
#endif
#ifdef TEST_USB_PD_INT
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_PORT_MAX_COUNT 1
#define CONFIG_USB_PD_TCPC
#define CONFIG_USB_PD_TCPM_STUB
#define CONFIG_SHA256
#define CONFIG_SW_CRC
#endif
#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \
defined(TEST_USB_PD_REV30)
#define CONFIG_USB_POWER_DELIVERY

105
test/usb_pd_int.c Normal file
View File

@ -0,0 +1,105 @@
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Test USB-PD interrupt task.
*/
#include "task.h"
#include "test_util.h"
#include "mock/tcpc_mock.h"
#include "mock/timer_mock.h"
#include "mock/usb_mux_mock.h"
#define PORT0 0
/* Install Mock TCPC and MUX drivers */
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.drv = &mock_tcpc_driver,
},
};
struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_MAX_COUNT] = {
{
.driver = &mock_usb_mux_driver,
}
};
static int deferred_resume_called;
void pd_deferred_resume(int port)
{
deferred_resume_called = 1;
}
static int num_events;
uint16_t tcpc_get_alert_status(void)
{
if (--num_events > 0)
return PD_STATUS_TCPC_ALERT_0;
else
return 0;
}
test_static int test_storm_not_triggered(void)
{
num_events = 100;
deferred_resume_called = 0;
schedule_deferred_pd_interrupt(PORT0);
task_wait_event(SECOND);
TEST_EQ(deferred_resume_called, 0, "%d");
return EC_SUCCESS;
}
test_static int test_storm_triggered(void)
{
num_events = 1000;
deferred_resume_called = 0;
schedule_deferred_pd_interrupt(PORT0);
task_wait_event(SECOND);
TEST_EQ(deferred_resume_called, 1, "%d");
return EC_SUCCESS;
}
test_static int test_storm_not_triggered_for_32bit_overflow(void)
{
int i;
timestamp_t time;
/* Ensure the MSB is 1 for overflow comparison tests */
time.val = 0xff000000;
force_time(time);
/*
* 100 events every second for 10 seconds should never trigger
* a shutdown call.
*/
for (i = 0; i < 10; ++i) {
num_events = 100;
deferred_resume_called = 0;
schedule_deferred_pd_interrupt(PORT0);
task_wait_event(SECOND);
TEST_EQ(deferred_resume_called, 0, "%d");
}
return EC_SUCCESS;
}
void before_test(void)
{
pd_set_suspend(PORT0, 0);
}
void run_test(void)
{
/* Let tasks settle down */
task_wait_event(MINUTE);
RUN_TEST(test_storm_not_triggered);
RUN_TEST(test_storm_triggered);
RUN_TEST(test_storm_not_triggered_for_32bit_overflow);
test_print_result();
}

8
test/usb_pd_int.mocklist Normal file
View File

@ -0,0 +1,8 @@
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#define CONFIG_TEST_MOCK_LIST \
MOCK(USB_MUX) \
MOCK(TCPC)

11
test/usb_pd_int.tasklist Normal file
View File

@ -0,0 +1,11 @@
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/**
* See CONFIG_TASK_LIST in config.h for details.
*/
#define CONFIG_TEST_TASK_LIST \
TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
TASK_TEST(PD_INT_C0, pd_interrupt_handler_task, 0, LARGER_TASK_STACK_SIZE)