Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp411511imu; Tue, 8 Jan 2019 23:14:08 -0800 (PST) X-Google-Smtp-Source: ALg8bN6hM/CFlbp0I7WRliNQzO9/kRXZhtTENcYxTJ8s0Adj0cYZlrwfR2wyYnDdhdz8fkJ3d19c X-Received: by 2002:a63:a553:: with SMTP id r19mr4364719pgu.53.1547018047977; Tue, 08 Jan 2019 23:14:07 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1547018047; cv=none; d=google.com; s=arc-20160816; b=FcFdj9omzwUnj55C+gB6kOhwcBmT/fp4kunSx9MIfnvHOF+Tp19UnHmsDpEGphs1hQ dIpRnVavaDMTdcY1Rg6Q5/Ne00y8w+mxiPm071wuEsYYzHij1BCt1BQttavljjh5HtDn 8Ntx/jQQMTKAbEWszILEyaFHdYZFJHqsEutyi0dt6pxtH3CyfICh6SE+KBoSf14QOQCe Aeocb0VKA7gkZauwLYFz/zbc6zl1plJXvXS6lBOwKUWPhwsqR2oUIWEJF7WUyehfN6UY 8Xp6WanWgkd9IMdsUZ+8Y4H3FN2KBJbb0jAQ+lBXoGjPc25Dgob3PMF+jy6sOtQWcCYM Oy/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=QucqvoBvXZvwNEMU04FO0+Z5+LhrEHNRUXrbDFmLgGw=; b=gdxVLJT1h1DeKxhk6uNTPPzrqll6jDgY1+ApAqtCMJtU05Pvk+F8pny95AjyMk7A1U 5ltgkRpH+5rrTG4hkZx2CV8g5tuPzK1C/wJY3Q4n3DNPeOK8DmalMzdbzAeUE0AO4+E7 EvrSDQVDwvd9X6G/re24sUEdOxTkQl1Nnwo1qhcShkj62ae2/z3WRA07y5uZt9jzGAfn RVYorTeW+7RlMdvryGF5VN8mUx4i0N9LfsLiF02Wwd3lvCvuiS0UuXC3xB1Xw7MrvqR3 jdOX7PyVvamn8hwbiDBFXL0DBNeimZEB+5fjRPnID5NRs/5uMMV6KBNp5xeORFoNBLFC ri1w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b="IK8L3t/n"; 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 r12si66907030pgf.22.2019.01.08.23.13.52; Tue, 08 Jan 2019 23:14:07 -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; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b="IK8L3t/n"; 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 S1730011AbfAIHLE (ORCPT + 99 others); Wed, 9 Jan 2019 02:11:04 -0500 Received: from perceval.ideasonboard.com ([213.167.242.64]:50458 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729986AbfAIHLB (ORCPT ); Wed, 9 Jan 2019 02:11:01 -0500 Received: from localhost.localdomain (unknown [96.44.9.117]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 20283586; Wed, 9 Jan 2019 08:10:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1547017860; bh=ef3JcGcK988dlprFJ+Vxh9N9Bm3TcEizRPSoR9vwbGQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IK8L3t/nyO2C2GqYG/LEPj3AwcZdZoHxzflaXKh1hzbmgvYk/e6hMatFpTwakv6gL HkG0WORNsFnRjCHKOJ43RAUhLJQk+jl+xfYVep6RNRtWPFyaMjrph2hMikKHZwOrN2 qHiR0Nma2wz9DrWh4q2Dd3JpcQUnJM9gIu80cIAM= From: Paul Elder To: laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Cc: Paul Elder , rogerq@ti.com, balbi@kernel.org, gregkh@linuxfoundation.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 1/4] usb: gadget: uvc: synchronize streamon/off with uvc_function_set_alt Date: Wed, 9 Jan 2019 02:10:36 -0500 Message-Id: <20190109071039.27702-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190109071039.27702-1-paul.elder@ideasonboard.com> References: <20190109071039.27702-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org If the streamon ioctl is issued while the stream is already on, then the kernel BUGs. This happens at the BUG_ON in uvc_video_alloc_requests within the call stack from the ioctl handler for VIDIOC_STREAMON. This could happen when uvc_function_set_alt 0 races and wins against uvc_v4l2_streamon, or when userspace neglects to issue the VIDIOC_STREAMOFF ioctl. To fix this, add two more uvc states: starting and stopping. Use these to prevent the racing, and to detect when VIDIOC_STREAMON is issued without previously issuing VIDIOC_STREAMOFF. Signed-off-by: Paul Elder --- Changes in v3: - add state guard to uvc_function_set_alt 1 - add documentation for newly added uvc states - reorder uvc states to more or less follow the flow diagram - add more state guards to ioctl handlers for streamon and streamoff Changes in v2: Nothing drivers/usb/gadget/function/f_uvc.c | 17 ++++++++---- drivers/usb/gadget/function/uvc.h | 37 ++++++++++++++++++++++++++ drivers/usb/gadget/function/uvc_v4l2.c | 26 ++++++++++++++++-- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8c99392df593..2ec3b73b2b75 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -317,26 +317,31 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) switch (alt) { case 0: - if (uvc->state != UVC_STATE_STREAMING) + if (uvc->state != UVC_STATE_STREAMING && + uvc->state != UVC_STATE_STARTING) return 0; if (uvc->video.ep) usb_ep_disable(uvc->video.ep); + uvc->state = UVC_STATE_STOPPING; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev, &v4l2_event); - uvc->state = UVC_STATE_CONNECTED; return 0; case 1: - if (uvc->state != UVC_STATE_CONNECTED) - return 0; - if (!uvc->video.ep) return -EINVAL; + if (uvc->state == UVC_STATE_STOPPING) + return -EINVAL; + + if (uvc->state != UVC_STATE_CONNECTED) + return 0; + uvcg_info(f, "reset UVC\n"); usb_ep_disable(uvc->video.ep); @@ -346,6 +351,8 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) return ret; usb_ep_enable(uvc->video.ep); + uvc->state = UVC_STATE_STARTING; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(&uvc->vdev, &v4l2_event); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 099d650082e5..f183e349499c 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -101,10 +101,47 @@ struct uvc_video { unsigned int fid; }; +/** + * enum uvc_state - the states of a struct uvc_device + * @UVC_STATE_DISCONNECTED: not connected state + * - transition to connected state on .set_alt + * @UVC_STATE_CONNECTED: connected state + * - transition to disconnected state on .disable + * and alloc + * - transition to starting state on .set_alt 1 + * @UVC_STATE_STARTING: starting state + * - transition to streaming state on streamon ioctl + * - transition to stopping state on set_alt 0 + * @UVC_STATE_STREAMING: streaming state + * - transition to stopping state on .set_alt 0 + * @UVC_STATE_STOPPING: stopping state + * - transition to connected on streamoff ioctl + * + * Diagram of state transitions: + * + * disable + * +---------------------------+ + * v | + * +--------------+ set_alt +-----------+ + * | DISCONNECTED | ---------> | CONNECTED | + * +--------------+ +-----------+ + * | ^ + * set_alt 1 | | streamoff + * +----------------------+ --------------------+ + * V | + * +----------+ streamon +-----------+ set_alt 0 +----------+ + * | STARTING | ----------> | STREAMING | -----------> | STOPPING | + * +----------+ +-----------+ +----------+ + * | ^ + * | set_alt 0 | + * +------------------------------------------------+ + */ enum uvc_state { UVC_STATE_DISCONNECTED, UVC_STATE_CONNECTED, + UVC_STATE_STARTING, UVC_STATE_STREAMING, + UVC_STATE_STOPPING, }; struct uvc_device { diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index a1183eccee22..97e624214287 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -197,17 +197,24 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (type != video->queue.queue.type) return -EINVAL; + if (uvc->state == UVC_STATE_STREAMING) + return 0; + + if (uvc->state != UVC_STATE_STARTING) + return -EINVAL; + /* Enable UVC video. */ ret = uvcg_video_enable(video, 1); if (ret < 0) return ret; + uvc->state = UVC_STATE_STREAMING; + /* * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ uvc_function_setup_continue(uvc); - uvc->state = UVC_STATE_STREAMING; return 0; } @@ -218,11 +225,26 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; + int ret; if (type != video->queue.queue.type) return -EINVAL; - return uvcg_video_enable(video, 0); + /* + * Check for connected state also because we want to reset buffers + * if this is called when the stream is already off. + */ + if (uvc->state != UVC_STATE_STOPPING && + uvc->state != UVC_STATE_CONNECTED) + return 0; + + ret = uvcg_video_enable(video, 0); + if (ret < 0) + return ret; + + uvc->state = UVC_STATE_CONNECTED; + + return 0; } static int -- 2.20.1