Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8aeb9733 authored by Elson Roy Serrao's avatar Elson Roy Serrao
Browse files

usb: f_qdss: Handle async completion of requests during qdss_close



When qdss_close is called we try to dequeue the pending and started
requests and then free those requests. But when async completion is
enabled the completion callback could be called after freeing up
that request which may lead to list corruption. Handle this by
ensuring that the requests are freed only after the completion
callback is done for all the requests.

Change-Id: I3e5b1bad06f281422a71c84a617feb65027c2c84
Signed-off-by: default avatarElson Roy Serrao <eserrao@codeaurora.org>
parent d66fecea
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@ static void usb_read_work_fn(struct work_struct *work)
						sizeof(*usb_req), GFP_KERNEL);
			if (!usb_req)
				return;
			init_completion(&usb_req->write_done);
			usb_req->sg = devm_kzalloc(tmcdrvdata->dev,
					sizeof(*(usb_req->sg)) * req_sg_num,
					GFP_KERNEL);
+2 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#define KMSG_COMPONENT "QDSS diag bridge"
@@ -108,6 +108,7 @@ static int qdss_create_buf_tbl(struct qdss_bridge_drvdata *drvdata)

		buf = kzalloc(drvdata->mtu, GFP_KERNEL);
		usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL);
		init_completion(&usb_req->write_done);

		entry->buf = buf;
		entry->usb_req = usb_req;
+8 −6
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * f_qdss.c -- QDSS function Driver
 *
 * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
 */

#include <linux/init.h>
@@ -241,6 +241,7 @@ static void qdss_write_complete(struct usb_ep *ep,
	if (!qdss->debug_inface_enabled)
		list_del(&req->list);
	list_add_tail(&req->list, list_pool);
	complete(&d_req->write_done);
	if (req->length != 0) {
		d_req->actual = req->actual;
		d_req->status = req->status;
@@ -925,10 +926,12 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
	req->sg = d_req->sg;
	req->num_sgs = d_req->num_sgs;
	req->num_mapped_sgs = d_req->num_mapped_sgs;
	reinit_completion(&d_req->write_done);
	if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) {
		spin_lock_irqsave(&qdss->lock, flags);
		/* Remove from queued pool and add back to data pool */
		list_move_tail(&req->list, &qdss->data_write_pool);
		complete(&d_req->write_done);
		spin_unlock_irqrestore(&qdss->lock, flags);
		pr_err("qdss usb_ep_queue failed\n");
		return -EIO;
@@ -993,6 +996,7 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
	unsigned long flags;
	int status;
	struct usb_request *req;
	struct qdss_request *d_req;

	pr_debug("%s\n", __func__);

@@ -1003,12 +1007,10 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
	while (!list_empty(&qdss->queued_data_pool)) {
		req = list_first_entry(&qdss->queued_data_pool,
				struct usb_request, list);
		d_req = req->context;
		spin_unlock_irqrestore(&qdss_lock, flags);
		if (usb_ep_dequeue(qdss->port.data, req)) {
			spin_lock_irqsave(&qdss_lock, flags);
			list_move_tail(&req->list, &qdss->data_write_pool);
			spin_unlock_irqrestore(&qdss_lock, flags);
		}
		usb_ep_dequeue(qdss->port.data, req);
		wait_for_completion(&d_req->write_done);
		spin_lock_irqsave(&qdss_lock, flags);
	}
	usb_qdss_free_req(ch);
+2 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2012-2013, 2017-2019 The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2013, 2017-2020 The Linux Foundation. All rights reserved.
 */

#ifndef __LINUX_USB_QDSS_H
@@ -20,6 +20,7 @@ struct qdss_request {
	struct scatterlist *sg;
	unsigned int num_sgs;
	unsigned int num_mapped_sgs;
	struct completion write_done;
};

struct usb_qdss_ch {