Received: by 10.223.176.5 with SMTP id f5csp1136322wra; Wed, 31 Jan 2018 01:44:42 -0800 (PST) X-Google-Smtp-Source: AH8x226MGx+TBAuHBajgYpalRoULJ7ETi4KaVdvMQ6lD4+Iaz0VbQcCqm11cIbT94E9PbMqnwyZ/ X-Received: by 10.98.159.25 with SMTP id g25mr33274683pfe.224.1517391882785; Wed, 31 Jan 2018 01:44:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517391882; cv=none; d=google.com; s=arc-20160816; b=ZRKeZrtGLj/FEQ1hryZrasT1P3T56VIxrANzzPllIVN8qdMmyd/dujOwr/SYn0Tod+ vYBPuf4Ri2G7q4Q2SobRBztecndGzCK2VkTTo6hpv0GyPCMJZrG97k8wMyv2kpsXHttU uSM7/yvsdNphwq6YfFLTgeD8KD48xf39v5YBgbHC5CFXSVU2fGWEz73XqplMx03tHPzO Um3TE1NzhnLQAJJVuw82GjhhD8npgFnD/DxkZBg2q+/mpYYClt7siLNlMQq3g71X6k9S Nioa7DAy9gT38x00O/VNuXqMAWy2iayXX8XIbzDtLwPN+9CrPvssz/axBcdqHMW3mpL2 H7PQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:message-id:date:subject:cc :to:from:arc-authentication-results; bh=AG3eQc5MLBrM65nsSAbahmvYYKIrGTFZ+J4F9GDZdl8=; b=VtvlrKmc6W0TYn+ifd3eJVMM2jnbHDQ/H1MSXPihBTqOZlRA1Cd0ARvxpfRir0wwKS DFL55xTmbAidupVu3iTI0Ku6USV57cHkbFfCk+4BjhpUrXNkotPeDxgsqA8l1gl8wea+ Sxw0vaPpGLaXFbTFe/jR1prCWAAjDwVT5RuVAhzD5E3G4bd6bP5ULtPXJNDxxZmwHErB oR0xLoRBwM/Lxmpxuq00LcmNalD5Cdr1iEpcOY/XTjea1uIuOrBS/B5UIoFam0T6bypQ wORztY7ltQZHlImLEoATp7F/hJnBNPgkSU3pSWN4so+JNe6ARYyO7Ka64m5CuQJ5erfG 7idg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 31-v6si1641079plz.201.2018.01.31.01.44.28; Wed, 31 Jan 2018 01:44:42 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753905AbeAaJh1 (ORCPT + 99 others); Wed, 31 Jan 2018 04:37:27 -0500 Received: from mail.windriver.com ([147.11.1.11]:47813 "EHLO mail.windriver.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751521AbeAaJhY (ORCPT ); Wed, 31 Jan 2018 04:37:24 -0500 Received: from ALA-HCA.corp.ad.wrs.com (ala-hca.corp.ad.wrs.com [147.11.189.40]) by mail.windriver.com (8.15.2/8.15.1) with ESMTPS id w0V9bAaQ007582 (version=TLSv1 cipher=AES128-SHA bits=128 verify=FAIL); Wed, 31 Jan 2018 01:37:11 -0800 (PST) Received: from pek-lpgtest5.wrs.com (128.224.153.85) by ALA-HCA.corp.ad.wrs.com (147.11.189.40) with Microsoft SMTP Server id 14.3.361.1; Wed, 31 Jan 2018 01:37:09 -0800 From: Haiqing Bai To: CC: , , , Subject: [PATCH] ohci-hcd: Fix race condition caused by ohci_urb_enqueue() and io_watchdog_func() Date: Wed, 31 Jan 2018 17:16:37 +0800 Message-ID: <1517390197-32323-1-git-send-email-Haiqing.Bai@windriver.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Content-Type: text/plain Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Running io_watchdog_func() while ohci_urb_enqueue() is running can cause a race condition where ohci->prev_frame_no is corrupted and the watchdog can mis-detect following error: ohci-platform 664a0800.usb: frame counter not updating; disabled ohci-platform 664a0800.usb: HC died; cleaning up Specifically, following scenario causes a race condition: 1. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 2. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 3. ohci_urb_enqueue() sets ohci->prev_frame_no to a frame number read by ohci_frame_no(ohci) 4. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 5. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section 6. Later, ohci_urb_enqueue() is called 7. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 8. The timer scheduled on step 4 expires and io_watchdog_func() runs 9. io_watchdog_func() calls spin_lock_irqsave(&ohci->lock, flags) and waits on it because ohci_urb_enqueue() is already in the critical section on step 7 10. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 11. ohci_urb_enqueue() sets ohci->prev_frame_no to new frame number read by ohci_frame_no(ohci) because the frame number proceeded between step 3 and 6 12. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 13. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section, then wake up io_watchdog_func() which is waiting on step 9 14. io_watchdog_func() enters the critical section 15. io_watchdog_func() calls ohci_frame_no(ohci) and set frame_no variable to the frame number 16. io_watchdog_func() compares frame_no and ohci->prev_frame_no On step 16, because this calling of io_watchdog_func() is scheduled on step 4, the frame number set in ohci->prev_frame_no is expected to the number set on step 3. However, ohci->prev_frame_no is overwritten on step 11. Because step 16 is executed soon after step 11, the frame number might not proceed, so ohci->prev_frame_no must equals to frame_no. To address above scenario, this patch introduces timer_running flag to ohci_hcd structure. Setting true to ohci->timer_running indicates io_watchdog_func() is scheduled or is running. ohci_urb_enqueue() checks the flag when it schedules the watchdog (step 4 and 12 above), so ohci->prev_frame_no is not overwritten while io_watchdog_func() is running. Author: Yoshida, Shigeru Signed-off-by: Haiqing Bai --- drivers/usb/host/ohci-hcd.c | 7 +++++++ drivers/usb/host/ohci-hub.c | 4 +++- drivers/usb/host/ohci.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index ee9676349333..2c7fa0c05854 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -233,10 +233,12 @@ static int ohci_urb_enqueue ( /* Start up the I/O watchdog timer, if it's not running */ if (!timer_pending(&ohci->io_watchdog) && list_empty(&ohci->eds_in_use) && + !ohci->timer_running && !(ohci->flags & OHCI_QUIRK_QEMU)) { ohci->prev_frame_no = ohci_frame_no(ohci); mod_timer(&ohci->io_watchdog, jiffies + IO_WATCHDOG_DELAY); + ohci->timer_running = 1; } list_add(&ed->in_use_list, &ohci->eds_in_use); @@ -501,6 +503,7 @@ static int ohci_init (struct ohci_hcd *ohci) return 0; timer_setup(&ohci->io_watchdog, io_watchdog_func, 0); + ohci->timer_running = 0; ohci->hcca = dma_alloc_coherent (hcd->self.controller, sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL); @@ -732,6 +735,7 @@ static void io_watchdog_func(struct timer_list *t) struct td *td, *td_start, *td_next; unsigned frame_no; unsigned long flags; + int timer_running = 0; spin_lock_irqsave(&ohci->lock, flags); @@ -841,10 +845,12 @@ static void io_watchdog_func(struct timer_list *t) &ohci->regs->donehead); mod_timer(&ohci->io_watchdog, jiffies + IO_WATCHDOG_DELAY); + timer_running = 1; } } done: + ohci->timer_running = timer_running; spin_unlock_irqrestore(&ohci->lock, flags); } @@ -973,6 +979,7 @@ static void ohci_stop (struct usb_hcd *hcd) if (quirk_nec(ohci)) flush_work(&ohci->nec_work); del_timer_sync(&ohci->io_watchdog); + ohci->timer_running = 0; ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_usb_reset(ohci); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index fb7aaa3b9d06..2aa21ee39cc2 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -311,8 +311,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) rc = ohci_rh_suspend (ohci, 0); spin_unlock_irq (&ohci->lock); - if (rc == 0) + if (rc == 0) { del_timer_sync(&ohci->io_watchdog); + ohci->timer_running = 0; + } return rc; } diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 508a803139dd..84f96558d667 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -427,6 +427,7 @@ struct ohci_hcd { unsigned wdh_cnt, prev_wdh_cnt; u32 prev_donehead; struct timer_list io_watchdog; + int timer_running; struct work_struct nec_work; /* Worker for NEC quirk */ -- 2.11.0