From patchwork Thu Oct 12 00:24:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Avichal Rakesh X-Patchwork-Id: 151632 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2908:b0:403:3b70:6f57 with SMTP id ib8csp899161vqb; Wed, 11 Oct 2023 17:25:22 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFOoPdiXSe9esAFgExD+2uG+MJO6/f+HjdeNpzA+Ie7I+E+gadeh/lk7T+2/2vglk8E5zr5 X-Received: by 2002:a17:902:a40e:b0:1c9:bd60:72a6 with SMTP id p14-20020a170902a40e00b001c9bd6072a6mr5799901plq.4.1697070321991; Wed, 11 Oct 2023 17:25:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697070321; cv=none; d=google.com; s=arc-20160816; b=LGAMAEH9xB6lmU5ptbwOvzbTz2G41SEJzenoANaGgFXLacE6W/DPzS6Wm0i7iZCuQ5 QLXATA1S9ANN+eW6j1K1G2X8SxL+AIrx89ZqUC6z4lTmE+qRFWJFo1aKzvbMGZDyQKPq jyQZ8Yj53HJ+22L/fa6I+iRWMAk6zMx3BXiluoF/2mSZZCJ16/BXElCwHIRyz2M5a1bv Lxh68Z5i+MhjfZRnnYiNmIdmQhbg5rxJ5xJotV3r/9zVd7MulMxHiiY+PXWl8Rl6NmBh a0vdXeokjLsE0uK14hOTEQ/ZUpE31k3mCX4jSAZ5+ZYLaiTS3nz9CzpvsuT6ILxeiCeQ Ze8A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=TG++fvWfitw/Sd3Hpgv4FLh87i4JPu5cxo2T/O4RJwY=; fh=0wk28KT8ebzzQgtMVSsY3TFcpC6qYdokWdikfW1Lhwk=; b=R9vMzeyUEGLfaMRf8rX0LMTQ0iBFHMG9d56uguM3IXvZEqgSDOSaYma0zWaDsxCsj4 HxUUpG0AaWm9t6hwY71of6hV2AlxB7BIDbJc98tQRVAYfiGJEBfzLq/+n6kyKgdNu1u2 Xp9laMo2tMbhT0MrIFfpNYCjFDBQTfxtRo2g1bVwBbNjbI2FxfijhbGiBKqxMeNoubI/ erHyLS+ACxFnoBZtIKotDXbjieBEkGdyLeYMnArLX1DunrhxGlWLXKvY0WwIydaKU7y9 Fe2A7Mj7fovEp7Pggg8UQj7/LXyNV/MnGwJRqSZDJ3zcoh4LgXtevS/8EWyNwYUB0h8x F5Kg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=IQ7LCc4d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from pete.vger.email (pete.vger.email. [23.128.96.36]) by mx.google.com with ESMTPS id i14-20020a170902c94e00b001c61bd7cee0si974427pla.211.2023.10.11.17.25.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 11 Oct 2023 17:25:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) client-ip=23.128.96.36; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=IQ7LCc4d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 9D589818FBD4; Wed, 11 Oct 2023 17:25:14 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233919AbjJLAY5 (ORCPT + 18 others); Wed, 11 Oct 2023 20:24:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40640 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231381AbjJLAY4 (ORCPT ); Wed, 11 Oct 2023 20:24:56 -0400 Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7F58B94 for ; Wed, 11 Oct 2023 17:24:55 -0700 (PDT) Received: by mail-pl1-x64a.google.com with SMTP id d9443c01a7336-1c9d140fb3eso3484595ad.3 for ; Wed, 11 Oct 2023 17:24:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1697070295; x=1697675095; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=TG++fvWfitw/Sd3Hpgv4FLh87i4JPu5cxo2T/O4RJwY=; b=IQ7LCc4dLlKSkhl/k/jDiI6c6qxEapg6q03XfT+vMZt/x6+16yOiUq4zLS8KnCRBEd s3beUpzLqnK3yJ6QavMqqgcsL6GbjizRR6HdI9Af32TX26zaTkAPDi4WeFNOkNxVTkME 2FyvtiLucNPxALbvDGfQSW4cpIIi6cQNXKvbyNOP//qbeDB9LL2BkVGHjuwBwClHXj7U Aa637zUHDVlepYSQH4bfiwxw5CrNnKeImblV621OlWgvJciTJDnfKOBv3Pp2b3IKAm4v NvZYsQTouwY4LkppeCC8nJ1bsOWhaaaZDPJrHw911uBRGrmQN9vJeMQ3ZyR59mYqRuJH 04IQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697070295; x=1697675095; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=TG++fvWfitw/Sd3Hpgv4FLh87i4JPu5cxo2T/O4RJwY=; b=SqaerRqrBkUw5ATmVN0fUtkaQEzMfhnQAg6hQBT4ABq9y2LchhcfBdtbsFrR4c6QBa 7LfwKW+LgPNcoRZcHAEWYy2kHX6UqPZSbs0AmSoEjD7QCS7OQjt3F2npcTolA3oqtjB4 MrIaQ6bkjizeNAE+GFSYVJDHHZ+nY1p3EHm4xpfFR8UpT2KZEJXYkdsR+iMye3Xj5bRv hfRO8AVf8eK47dC78IFGVrliqREZE5FRWeBRL0D0BbXJ9I/vGzWK0SVTKJ4h04v9w1oZ mXZfG44yWJvjvmmL3o0X0I1/iNR5PElC2V4feFLXBYvxJYXXyFk12aSKugMyNc8EkkZu lNTA== X-Gm-Message-State: AOJu0YyGj0VH/93ARmPK/5jEm9fkg1T1SHH2Sl91BBWvdWuEln6dm4rH jmuIqPDxr8M/Uz1cl7gbvHSQ0Tiysjws X-Received: from hi-h2o-specialist.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3cef]) (user=arakesh job=sendgmr) by 2002:a17:90b:2391:b0:27d:1cfb:7185 with SMTP id mr17-20020a17090b239100b0027d1cfb7185mr21475pjb.9.1697070294985; Wed, 11 Oct 2023 17:24:54 -0700 (PDT) Date: Wed, 11 Oct 2023 17:24:49 -0700 In-Reply-To: <20230930184821.310143-1-arakesh@google.com> Mime-Version: 1.0 References: <20230930184821.310143-1-arakesh@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231012002451.254737-1-arakesh@google.com> Subject: [PATCH v4 1/3] usb: gadget: uvc: prevent use of disabled endpoint From: Avichal Rakesh To: arakesh@google.com, dan.scally@ideasonboard.com, laurent.pinchart@ideasonboard.com, m.grzeschik@pengutronix.de Cc: etalvala@google.com, gregkh@linuxfoundation.org, jchowdhary@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org X-Spam-Status: No, score=-4.8 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_SBL_CSS,SPF_HELO_NONE,SPF_PASS, USER_IN_DEF_DKIM_WL autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Wed, 11 Oct 2023 17:25:14 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1778501398075540037 X-GMAIL-MSGID: 1779507210029008949 Currently the set_alt callback immediately disables the endpoint and queues the v4l2 streamoff event. However, as the streamoff event is processed asynchronously, it is possible that the video_pump thread attempts to queue requests to an already disabled endpoint. This change moves disabling usb endpoint to the end of streamoff event callback. As the endpoint's state can no longer be used, video_pump is now guarded by uvc->state as well. To be consistent with the actual streaming state, uvc->state is now toggled between CONNECTED and STREAMING from the v4l2 event callback only. Link: https://lore.kernel.org/20230615171558.GK741@pendragon.ideasonboard.com/ Link: https://lore.kernel.org/20230531085544.253363-1-dan.scally@ideasonboard.com/ Reviewed-by: Michael Grzeschik Signed-off-by: Avichal Rakesh --- v1 -> v2: Rebased to ToT and reworded commit message. v2 -> v3: Fix email threading goof-up v3 -> v4: Address review comments & re-rebase to ToT drivers/usb/gadget/function/f_uvc.c | 11 +++++------ drivers/usb/gadget/function/f_uvc.h | 2 +- drivers/usb/gadget/function/uvc.h | 2 +- drivers/usb/gadget/function/uvc_v4l2.c | 21 ++++++++++++++++++--- drivers/usb/gadget/function/uvc_video.c | 3 ++- 5 files changed, 27 insertions(+), 12 deletions(-) -- 2.42.0.609.gbb76f46606-goog diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index faa398109431..ae08341961eb 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } -void uvc_function_setup_continue(struct uvc_device *uvc) +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + if (disable_ep && uvc->video.ep) + usb_ep_disable(uvc->video.ep); + usb_composite_setup_continue(cdev); } @@ -337,15 +340,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) - usb_ep_disable(uvc->video.ep); - 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; + return USB_GADGET_DELAYED_STATUS; case 1: if (uvc->state != UVC_STATE_CONNECTED) diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 1db972d4beeb..e7f9f13f14dc 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -11,7 +11,7 @@ struct uvc_device; -void uvc_function_setup_continue(struct uvc_device *uvc); +void uvc_function_setup_continue(struct uvc_device *uvc, int disale_ep); void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 6751de8b63ad..989bc6b4e93d 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -177,7 +177,7 @@ struct uvc_file_handle { * Functions */ -extern void uvc_function_setup_continue(struct uvc_device *uvc); +extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 3f0a9795c0d4..c0d77564a204 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -451,7 +451,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ - uvc_function_setup_continue(uvc); + uvc_function_setup_continue(uvc, 0); uvc->state = UVC_STATE_STREAMING; return 0; @@ -463,11 +463,19 @@ 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 = 0; if (type != video->queue.queue.type) return -EINVAL; - return uvcg_video_enable(video, 0); + uvc->state = UVC_STATE_CONNECTED; + ret = uvcg_video_enable(video, 0); + if (ret < 0) { + return ret; + } + + uvc_function_setup_continue(uvc, 1); + return 0; } static int @@ -500,6 +508,14 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); + /* + * Drop uvc->state to CONNECTED if it was streaming before. + * This ensures that the usb_requests are no longer queued + * to the controller. + */ + if (uvc->state == UVC_STATE_STREAMING) + uvc->state = UVC_STATE_CONNECTED; + uvcg_video_enable(&uvc->video, 0); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; @@ -647,4 +663,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = { .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; - diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 91af3b1ef0d4..c334802ac0a4 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -384,13 +384,14 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; + struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (video->ep->enabled) { + while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { /* * Retrieve the first available USB request, protected by the * request lock. From patchwork Thu Oct 12 00:24:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Avichal Rakesh X-Patchwork-Id: 151631 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2908:b0:403:3b70:6f57 with SMTP id ib8csp899079vqb; Wed, 11 Oct 2023 17:25:09 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGnahei7XOLW2W6jVjNuDkB6ZHE8Qa3RqS+avVPvwe3lP2+vRh3+jWmbq0LdPenic96Iszz X-Received: by 2002:a05:6a00:1d22:b0:693:38c5:4d6d with SMTP id a34-20020a056a001d2200b0069338c54d6dmr25181127pfx.2.1697070309493; Wed, 11 Oct 2023 17:25:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697070309; cv=none; d=google.com; s=arc-20160816; b=vAdelN0ozqFrDg1Frj52aAGu72MZMN0+mHhJ4iLvbvcNmzMBS7fvus93O5lYdyVyOp agwVxfYL9pn1dDta115v+RtV6ZhCoBF3rk2YSnC/I+QGFGaqHK9D5QQkmI7shmUsOs8k tTJt1789K5POHZ3ZgyiI9g8X9MeO3qSSwX4rYWbpu/35FSEjHjmgunCW+WqCDWx1DMT9 7KcUjfgakHegacBwKyEbnbkJcRUobz94tDLV25gizO2Yz+NPLU2KFc6XvsOKY3zPOk9n cyXvAXRe9jOkSQp2K3qVdiNPQ/Epuq4oJhyl4/JNPraZkidhfn8WlDoKJ3GQ/t+Vqh8y NIBw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=btfnS5sl+NHrJw3NusDcQHMvqgT7weLrRVmIEylNtmA=; fh=0wk28KT8ebzzQgtMVSsY3TFcpC6qYdokWdikfW1Lhwk=; b=qjrOdcoNWFk4KNj6g0b2kiy4JSOQf+utp6fQDs52qygAivOvLRnqlH2ZDpF1Hqbm/G tytJ2nAqVSAh1rGJxkEeHOcyPe+42mU/mPhu2qvIxX86n9y+94Ot82HP3wEyaLuKdzUa kIh0scPt4lnsFnxZYZpQzYtghoTjG82kgf4JfOEHCLV12L/bLM4hEB9gqVrpwn0WGZGI n6+tBGzOEmwJoiRxCRktz332CmZnzSqA0jccm4AA86Cly4/BDGKJO8cFMCiArqj6bLlS YptMOmU5FUkeag9eSXb5zWJ/SxAjzx7TiPNa+GrZCXmDFqDjlYU30gasWuGK6VaSdOFn M7HQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=paV3ju6q; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from snail.vger.email (snail.vger.email. [23.128.96.37]) by mx.google.com with ESMTPS id w2-20020a654102000000b0059c78eb3fe7si871574pgp.838.2023.10.11.17.25.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 11 Oct 2023 17:25:09 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) client-ip=23.128.96.37; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=paV3ju6q; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id C5FE181C7AB1; Wed, 11 Oct 2023 17:25:08 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1376605AbjJLAZC (ORCPT + 18 others); Wed, 11 Oct 2023 20:25:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40646 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231381AbjJLAZA (ORCPT ); Wed, 11 Oct 2023 20:25:00 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 54D5294 for ; Wed, 11 Oct 2023 17:24:58 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id 41be03b00d2f7-56f75e70190so268398a12.3 for ; Wed, 11 Oct 2023 17:24:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1697070298; x=1697675098; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=btfnS5sl+NHrJw3NusDcQHMvqgT7weLrRVmIEylNtmA=; b=paV3ju6qfLYkT/XRvfoREwdzlTmtv+EQVM6If6Jz5RSfaMh59/TTOqWhwXtcrREBqt 1O2fg9x5NuK5qJUT+9zqsVKrhcE61l64A7ivbHKEgQbCyrxsh4y3tWz2plEgZdtKOVBt kyQgsLzSsUfvMLK74CwYQtkCEQZMt0UDQQb+ySVimOO8khsKuiTWOhRFt122GpTIMVQI DP8756LJS9yRJTkUFrmJ3B/kxcNpQqRmjoJlZdbw97+M5MzIssqCUv6DzN1Swrh2y5wh Zy/tHVTEZaCuhphw+Vq5AkbPw1CG5k/m6Vh0yHiFMHsAl54sfharjvqUhhbtrkvx9CQA TTrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697070298; x=1697675098; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=btfnS5sl+NHrJw3NusDcQHMvqgT7weLrRVmIEylNtmA=; b=Fmskpd2XqGt926N6S3NB5TPRxQKSPJ+p2Kh+3tzmWB8Jr3dprUCTqVab4zkY9sCirq 99ecZ4Sv3F4NqkYDeYBA7QerAkc9rFktlrOndLdvHJTYqNz+RiXjczVuRmLr5JK2LZMV 2Pvp6eHPmT6FK7Qeet5in5IoXOJfte3t141OajEiBoFIVZE5/gTknI5YbmXipsEb9j2n xoNkLAvZ2q6f/SantGH1nybKNJrxEkrgC08VomAHzMd2RUgheKuWuUelaTlIqPFHHzy2 qPkY1m3tiu/th8A4JaffmDmRtJvgKcwOcDpnHqorGhOtJnznZxvBAkojDHE5okEeVjT4 ajvg== X-Gm-Message-State: AOJu0YxxLGc19eEb7VU4REPMVj67GJivv49CkRhhh4GvUDLL4Pt3EJx4 bnWuj82tQNBMqWAnjA/HKoIbN51mJc9A X-Received: from hi-h2o-specialist.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3cef]) (user=arakesh job=sendgmr) by 2002:a63:2955:0:b0:57e:2432:b384 with SMTP id bu21-20020a632955000000b0057e2432b384mr380748pgb.0.1697070297859; Wed, 11 Oct 2023 17:24:57 -0700 (PDT) Date: Wed, 11 Oct 2023 17:24:50 -0700 In-Reply-To: <20231012002451.254737-1-arakesh@google.com> Mime-Version: 1.0 References: <20230930184821.310143-1-arakesh@google.com> <20231012002451.254737-1-arakesh@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231012002451.254737-2-arakesh@google.com> Subject: [PATCH v4 2/3] usb: gadget: uvc: Allocate uvc_requests one at a time From: Avichal Rakesh To: arakesh@google.com, dan.scally@ideasonboard.com, laurent.pinchart@ideasonboard.com, m.grzeschik@pengutronix.de Cc: etalvala@google.com, gregkh@linuxfoundation.org, jchowdhary@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (snail.vger.email [0.0.0.0]); Wed, 11 Oct 2023 17:25:08 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1778501399086666798 X-GMAIL-MSGID: 1779507197107310274 Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This patch is 1 of 2 patches addressing the use-after-free issue. Instead of bulk allocating all uvc_requests as an array, this patch allocates uvc_requests one at a time, which should allows for similar granularity when deallocating the uvc_requests. This patch has no functional changes other than allocating each uvc_request separately, and similarly freeing each of them separately. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Suggested-by: Michael Grzeschik Reviewed-by: Michael Grzeschik Signed-off-by: Avichal Rakesh --- v1 -> v2: Rebased to ToT v2 -> v3: Fix email threading goof-up v3 -> v4: Address review comments & re-rebase to ToT drivers/usb/gadget/function/uvc.h | 3 +- drivers/usb/gadget/function/uvc_video.c | 87 ++++++++++++++----------- 2 files changed, 50 insertions(+), 40 deletions(-) -- 2.42.0.609.gbb76f46606-goog diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 989bc6b4e93d..993694da0bbc 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -81,6 +81,7 @@ struct uvc_request { struct sg_table sgt; u8 header[UVCG_REQUEST_HEADER_LEN]; struct uvc_buffer *last_buf; + struct list_head list; }; struct uvc_video { @@ -102,7 +103,7 @@ struct uvc_video { /* Requests */ unsigned int req_size; - struct uvc_request *ureq; + struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; spinlock_t req_lock; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c334802ac0a4..b62b3de79153 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,24 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +static void +uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) +{ + sg_free_table(&ureq->sgt); + if (ureq->req && ep) { + usb_ep_free_request(ep, ureq->req); + ureq->req = NULL; + } + + kfree(ureq->req_buffer); + ureq->req_buffer = NULL; + + if (!list_empty(&ureq->list)) + list_del_init(&ureq->list); + + kfree(ureq); +} + static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) { int ret; @@ -293,27 +311,12 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) static int uvc_video_free_requests(struct uvc_video *video) { - unsigned int i; + struct uvc_request *ureq, *temp; - if (video->ureq) { - for (i = 0; i < video->uvc_num_requests; ++i) { - sg_free_table(&video->ureq[i].sgt); - - if (video->ureq[i].req) { - usb_ep_free_request(video->ep, video->ureq[i].req); - video->ureq[i].req = NULL; - } - - if (video->ureq[i].req_buffer) { - kfree(video->ureq[i].req_buffer); - video->ureq[i].req_buffer = NULL; - } - } - - kfree(video->ureq); - video->ureq = NULL; - } + list_for_each_entry_safe(ureq, temp, &video->ureqs, list) + uvc_video_free_request(ureq, video->ep); + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); video->req_size = 0; return 0; @@ -322,6 +325,7 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + struct uvc_request *ureq; unsigned int req_size; unsigned int i; int ret = -ENOMEM; @@ -332,29 +336,32 @@ uvc_video_alloc_requests(struct uvc_video *video) * max_t(unsigned int, video->ep->maxburst, 1) * (video->ep->mult); - video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); - if (video->ureq == NULL) - return -ENOMEM; + INIT_LIST_HEAD(&video->ureqs); + for (i = 0; i < video->uvc_num_requests; i++) { + ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); + if (ureq == NULL) + goto error; + INIT_LIST_HEAD(&ureq->list); + list_add_tail(&ureq->list, &video->ureqs); - for (i = 0; i < video->uvc_num_requests; ++i) { - video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); - if (video->ureq[i].req_buffer == NULL) + ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + if (ureq->req_buffer == NULL) goto error; - video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); - if (video->ureq[i].req == NULL) + ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (ureq->req == NULL) goto error; - video->ureq[i].req->buf = video->ureq[i].req_buffer; - video->ureq[i].req->length = 0; - video->ureq[i].req->complete = uvc_video_complete; - video->ureq[i].req->context = &video->ureq[i]; - video->ureq[i].video = video; - video->ureq[i].last_buf = NULL; + ureq->req->buf = ureq->req_buffer; + ureq->req->length = 0; + ureq->req->complete = uvc_video_complete; + ureq->req->context = ureq; + ureq->video = video; + ureq->last_buf = NULL; - list_add_tail(&video->ureq[i].req->list, &video->req_free); + list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ - sg_alloc_table(&video->ureq[i].sgt, + sg_alloc_table(&ureq->sgt, DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } @@ -489,8 +496,8 @@ static void uvcg_video_pump(struct work_struct *work) */ int uvcg_video_enable(struct uvc_video *video, int enable) { - unsigned int i; int ret; + struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -502,9 +509,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable) cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - for (i = 0; i < video->uvc_num_requests; ++i) - if (video->ureq && video->ureq[i].req) - usb_ep_dequeue(video->ep, video->ureq[i].req); + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->req) + usb_ep_dequeue(video->ep, ureq->req); + } uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); @@ -536,6 +544,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); From patchwork Thu Oct 12 00:24:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Avichal Rakesh X-Patchwork-Id: 151633 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2908:b0:403:3b70:6f57 with SMTP id ib8csp899238vqb; Wed, 11 Oct 2023 17:25:32 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH0u0drsZuO3sIYYAdqZ26q2h5ZmetrFmYU0Ei2DF7q8mtDyOTrRm5R8A6BGDaDON5gwCe2 X-Received: by 2002:a17:902:e5d2:b0:1c4:1cd3:8062 with SMTP id u18-20020a170902e5d200b001c41cd38062mr25607494plf.2.1697070331820; Wed, 11 Oct 2023 17:25:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697070331; cv=none; d=google.com; s=arc-20160816; b=e9x2nh7cLVF92T3IER5jBVtaLYD9RW56b+PWylIqM3EXLiVfLeRZv8rXwhQmTdg1ie Wz1BcgaxhE3oht3h6cWZhsPthv54aUsRP0pLd7gMpWbAM3O5j/KGYLA4cxuB2tAcf+3Q +6mrACcDsvdOpAMgqIByzcQAsLKlgXwoV3o2lnP8I2QXOBI5ctdTImxxpMkdagG8t+7W q9CwbEtxA/ZugI8erF5X5E3aF933VqAmRHrlKekDDOGKOZLDQeCuVA8bJX7mAlAGqGU1 3Yl/IdqC03dLIsQVf+QCYTWS40eqWGxhddxqAEbIVDN42aMbpiOrtNGoYa63wrh9lrsz kVzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=cgopB/iDnieNYkK7XiJQZLx39wNfXfhXvxUWXbNmypA=; fh=0wk28KT8ebzzQgtMVSsY3TFcpC6qYdokWdikfW1Lhwk=; b=Iws84+wGpewSCScThwleeaN/sCBD5RWLlbfP0YlppbfSTgKwohxgo9ROShKBP9/Crh RMNztVDpPL07R1XJLmvpGoU6sa8lTmDlrdHTz4TEOtaynkXgTpnvUflCOmm6PorzMLom B4itzxkWc4GGo0+CIDVIBtSp0i1cPWpab14GSOM8y9n4tFGI6sImjDg0TquXPE6Rud+I TX0cFK5i+duCsvkjUT8ipD940d39p3yoGDxsyrLWfxRCMyyvruvudUNl/bI+wRgq0jzF 9jRV3CLhKKG1bR6nG42GnOWSAtcw6+ilF2KluootD/toZX++7vNZs9hhNmo7JL9xVG3J UU/g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=1ljthnVe; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from pete.vger.email (pete.vger.email. [2620:137:e000::3:6]) by mx.google.com with ESMTPS id ik24-20020a170902ab1800b001c9b94ed143si820570plb.623.2023.10.11.17.25.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 11 Oct 2023 17:25:31 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) client-ip=2620:137:e000::3:6; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=1ljthnVe; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id E98C7818FBD4; Wed, 11 Oct 2023 17:25:28 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235229AbjJLAZI (ORCPT + 18 others); Wed, 11 Oct 2023 20:25:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235217AbjJLAZD (ORCPT ); Wed, 11 Oct 2023 20:25:03 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B1BD294 for ; Wed, 11 Oct 2023 17:25:01 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-5a7bcbb95b2so6191867b3.3 for ; Wed, 11 Oct 2023 17:25:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1697070301; x=1697675101; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=cgopB/iDnieNYkK7XiJQZLx39wNfXfhXvxUWXbNmypA=; b=1ljthnVeOZf9UjDqB2Sp540FxxcFM5z7ZblmA4a0xvcS33PJ195O3wsWZCOw29IJtP mCvQb5GODgwwk2qLLT2TiRUdBSEvMnjFnsFDD56aVCuYuTfECjzVfiyEAXSWPbSeK36y gnbk58j9+uynbI0f9l6ZPcrp984txAJzYuaYuT0GuSJdy3jp5QiHc24BY1prCr0E8LcF PCiay15W+dLKXHPdHNdEQvDNzLs4b3p/Z4h77ZAH8KiUg6sKhgOVI3opMm2vdDb6OaRY g7Q4fmk/ZLHUne/OMLc4gqIC9/Jd2GtlIAMJ68uh1qfCb/o93ARA+E8FBtI724HU5dXX QaWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697070301; x=1697675101; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=cgopB/iDnieNYkK7XiJQZLx39wNfXfhXvxUWXbNmypA=; b=Y/DuqGlpqAeMbBfH43nCGRv9ATw81VFcl1sN7zhYF62lGfuaPpMCK3MB4tYsiSEten 7sdO1gl8Ca6Y0JBuL9+eiNhhZbOTMDjFUaAR928V0Wja2EU7qYvTn4UodITLXbSKGnXo M4NELXBaNp+aw1QdAJMi0v1zcpDH3Jf4HAn4kDZQYNWP7zo8mvJjSJboT8mTha8Nitam MYEhABgx5IEr5gKzPJn6MQDFyZlfn0++W9TMPVrsHFe2eEcFyRF7jLyJJ1/jY1amMS5s xZpAYdjY2TNBDVNtpXDjya3ol4tIDJNWQgIhO+dvjc0fmsEG1hfoYhjP65DWWcrQpsMZ i9+g== X-Gm-Message-State: AOJu0YzSwooV0ccBMX1qOmGQnDGdKgyRptthNPL4Bb9/lzs4Clm9eUWA pHb57XTCOdgBhE184ifCv6ncRsUXtMjn X-Received: from hi-h2o-specialist.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3cef]) (user=arakesh job=sendgmr) by 2002:a81:ac05:0:b0:5a7:ccf3:3f28 with SMTP id k5-20020a81ac05000000b005a7ccf33f28mr112624ywh.0.1697070300856; Wed, 11 Oct 2023 17:25:00 -0700 (PDT) Date: Wed, 11 Oct 2023 17:24:51 -0700 In-Reply-To: <20231012002451.254737-1-arakesh@google.com> Mime-Version: 1.0 References: <20230930184821.310143-1-arakesh@google.com> <20231012002451.254737-1-arakesh@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231012002451.254737-3-arakesh@google.com> Subject: [PATCH v4 3/3] usb: gadget: uvc: Fix use-after-free for inflight usb_requests From: Avichal Rakesh To: arakesh@google.com, dan.scally@ideasonboard.com, laurent.pinchart@ideasonboard.com, m.grzeschik@pengutronix.de Cc: etalvala@google.com, gregkh@linuxfoundation.org, jchowdhary@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org X-Spam-Status: No, score=-4.8 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_SBL_CSS,SPF_HELO_NONE,SPF_PASS, USER_IN_DEF_DKIM_WL autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Wed, 11 Oct 2023 17:25:29 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1778517869169821913 X-GMAIL-MSGID: 1779507219855014692 Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This is patch 2 of 2 in fixing the use-after-free issue. It adds a new flag to uvc_video to track when frames and requests should be flowing. When disabling the video stream, the flag is tripped and, instead of de-allocating all uvc_requests and usb_requests, the gadget driver only de-allocates those usb_requests that are currently owned by it (as present in req_free). Other usb_requests are left untouched until their completion handler is called which takes care of freeing the usb_request and its corresponding uvc_request. Now that uvc_video does not depends on uvc->state, this patch removes unnecessary upates to uvc->state that were made to accomodate uvc_video logic. This should ensure that uvc gadget driver never accidentally de-allocates a usb_request that it doesn't own. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Suggested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Reviewed-by: Michael Grzeschik Tested-by: Michael Grzeschik --- v1 -> v2: Rebased to ToT, and fixed deadlock reported in https://lore.kernel.org/all/ZRv2UnKztgyqk2pt@pengutronix.de/ v2 -> v3: Fix email threading goof-up v3 -> v4: re-rebase to ToT & moved to a uvc_video level lock as discussed in https://lore.kernel.org/b14b296f-2e08-4edf-aeea-1c5b621e2d0c@google.com/ drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_v4l2.c | 12 +- drivers/usb/gadget/function/uvc_video.c | 156 +++++++++++++++++++----- 3 files changed, 128 insertions(+), 41 deletions(-) -- 2.42.0.609.gbb76f46606-goog diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 993694da0bbc..be0d012aa244 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -102,6 +102,7 @@ struct uvc_video { unsigned int uvc_num_requests; /* Requests */ + bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index c0d77564a204..ded7d33c2a52 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -451,8 +451,8 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ - uvc_function_setup_continue(uvc, 0); uvc->state = UVC_STATE_STREAMING; + uvc_function_setup_continue(uvc, 0); return 0; } @@ -468,12 +468,12 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (type != video->queue.queue.type) return -EINVAL; - uvc->state = UVC_STATE_CONNECTED; ret = uvcg_video_enable(video, 0); if (ret < 0) { return ret; } + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; } @@ -508,14 +508,6 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); - /* - * Drop uvc->state to CONNECTED if it was streaming before. - * This ensures that the usb_requests are no longer queued - * to the controller. - */ - if (uvc->state == UVC_STATE_STREAMING) - uvc->state = UVC_STATE_CONNECTED; - uvcg_video_enable(&uvc->video, 0); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index b62b3de79153..05b89b5b6c48 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,9 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +/** + * Must be called with req_lock held as it modifies the list ureq is held in + */ static void uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) { @@ -271,9 +274,25 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_request *ureq = req->context; struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; - struct uvc_device *uvc = video->uvc; + struct uvc_buffer *last_buf = NULL; unsigned long flags; + spin_lock_irqsave(&video->req_lock, flags); + if (!video->is_enabled) { + /* + * When is_enabled is false, uvc_video_disable ensures that + * in-flight uvc_buffers are returned, so we can safely + * call free_request without worrying about last_buf. + */ + uvc_video_free_request(ureq, ep); + spin_unlock_irqrestore(&video->req_lock, flags); + return; + } + + last_buf = ureq->last_buf; + ureq->last_buf = NULL; + spin_unlock_irqrestore(&video->req_lock, flags); + switch (req->status) { case 0: break; @@ -295,17 +314,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) uvcg_queue_cancel(queue, 0); } - if (ureq->last_buf) { - uvcg_complete_buffer(&video->queue, ureq->last_buf); - ureq->last_buf = NULL; + if (last_buf) { + spin_lock_irqsave(&queue->irqlock, flags); + uvcg_complete_buffer(&video->queue, last_buf); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); - spin_unlock_irqrestore(&video->req_lock, flags); - - if (uvc->state == UVC_STATE_STREAMING) + /* + * Video stream might have been disabled while we were + * processing the current usb_request. So make sure + * we're still streaming before queueing the usb_request + * back to req_free + */ + if (video->is_enabled) { + list_add_tail(&req->list, &video->req_free); queue_work(video->async_wq, &video->pump); + } else { + uvc_video_free_request(ureq, ep); + } + spin_unlock_irqrestore(&video->req_lock, flags); } static int @@ -391,20 +419,22 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; - struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { + while(true) { + if (!video->ep->enabled) + return; + /* - * Retrieve the first available USB request, protected by the - * request lock. + * Check is_enabled and retrieve the first available USB + * request, protected by the request lock. */ spin_lock_irqsave(&video->req_lock, flags); - if (list_empty(&video->req_free)) { + if (!video->is_enabled || list_empty(&video->req_free)) { spin_unlock_irqrestore(&video->req_lock, flags); return; } @@ -486,9 +516,78 @@ static void uvcg_video_pump(struct work_struct *work) return; spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); + if (video->is_enabled) + list_add_tail(&req->list, &video->req_free); + else + uvc_video_free_request(req->context, video->ep); + spin_unlock_irqrestore(&video->req_lock, flags); +} + +/* + * Disable video stream + */ +static int +uvcg_video_disable(struct uvc_video *video) { + unsigned long flags; + struct list_head inflight_bufs; + struct usb_request *req, *temp; + struct uvc_buffer *buf, *btemp; + struct uvc_request *ureq, *utemp; + + INIT_LIST_HEAD(&inflight_bufs); + spin_lock_irqsave(&video->req_lock, flags); + video->is_enabled = false; + + /* + * Remove any in-flight buffers from the uvc_requests + * because we want to return them before cancelling the + * queue. This ensures that we aren't stuck waiting for + * all complete callbacks to come through before disabling + * vb2 queue. + */ + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->last_buf) { + list_add_tail(&ureq->last_buf->queue, &inflight_bufs); + ureq->last_buf = NULL; + } + } spin_unlock_irqrestore(&video->req_lock, flags); - return; + + cancel_work_sync(&video->pump); + uvcg_queue_cancel(&video->queue, 0); + + spin_lock_irqsave(&video->req_lock, flags); + /* + * Remove all uvc_reqeusts from from ureqs with list_del_init + * This lets uvc_video_free_request correctly identify + * if the uvc_request is attached to a list or not when freeing + * memory. + */ + list_for_each_entry_safe(ureq, utemp, &video->ureqs, list) + list_del_init(&ureq->list); + + list_for_each_entry_safe(req, temp, &video->req_free, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); + } + + INIT_LIST_HEAD(&video->ureqs); + INIT_LIST_HEAD(&video->req_free); + video->req_size = 0; + spin_unlock_irqrestore(&video->req_lock, flags); + + /* + * Return all the video buffers before disabling the queue. + */ + spin_lock_irqsave(&video->queue.irqlock, flags); + list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) { + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + + uvcg_queue_enable(&video->queue, 0); + return 0; } /* @@ -497,28 +596,22 @@ static void uvcg_video_pump(struct work_struct *work) int uvcg_video_enable(struct uvc_video *video, int enable) { int ret; - struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, "Video enable failed, device is uninitialized.\n"); return -ENODEV; } - - if (!enable) { - cancel_work_sync(&video->pump); - uvcg_queue_cancel(&video->queue, 0); - - list_for_each_entry(ureq, &video->ureqs, list) { - if (ureq->req) - usb_ep_dequeue(video->ep, ureq->req); - } - - uvc_video_free_requests(video); - uvcg_queue_enable(&video->queue, 0); - return 0; - } - + if (!enable) + return uvcg_video_disable(video); + + /* + * Safe to access request related fields without req_lock because + * this is the only thread currently active, and no other + * request handling thread will become active until this function + * returns. + */ + video->is_enabled = true; if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; @@ -544,6 +637,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + video->is_enabled = false; INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock);