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

Commit 02ec74e6 authored by Pratham Pratap's avatar Pratham Pratap
Browse files

sound: usb: Ensure proper cleanup of uaudio_dev under all scenarios



Consider a case where chip is freed before disabling the audio
channel. This can happen when usb_audio_disconnect is called due
to USB DevFS proc_disconnect_claim ioctl. usb_audio_disconnect
will call uaudio_disconnect_cb which will wait for in_use to be
false to cleanup the uaudio_dev. If in_use never becomes false
and the wait_event is interrupted by some other signal then driver
bails out esrly from here and doesn't cleanup the uaudio_dev. Since
uaudio_dev_release is responsible for clearing the in_use based on
stream disable call, fix the cyclic dependency here on
uaudio_dev_release and uaudio_dev_cleanup by adding timeout in
wait_event and allowing dev_cleanup to happen from uaudio_disconnect_cb.
If disable stream request comes after this, handle_uaudio_stream_req
will still go ahead and try to find substream of the card but will
go to error path since card is already disconnected. This will set
the return value to -ENODEV but in the error path driver is not
checking for the correct return value and trying to access chip again.
Fix this by adding one more check for -ENODEV in the error handling path.

Change-Id: Ie11ad162f02c46878eb2663bf21cbafa54a62b0a
Signed-off-by: default avatarPratham Pratap <prathampratap@codeaurora.org>
parent 5537f3dc
Loading
Loading
Loading
Loading
+21 −14
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-License-Identifier: GPL-2.0-only
/*
/*
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
 */
 */


#include <linux/module.h>
#include <linux/module.h>
@@ -31,6 +31,7 @@
#define BUS_INTERVAL_FULL_SPEED 1000 /* in us */
#define BUS_INTERVAL_FULL_SPEED 1000 /* in us */
#define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */
#define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */
#define MAX_BINTERVAL_ISOC_EP 16
#define MAX_BINTERVAL_ISOC_EP 16
#define DEV_RELEASE_WAIT_TIMEOUT 10000 /* in ms */


#define SND_PCM_CARD_NUM_MASK 0xffff0000
#define SND_PCM_CARD_NUM_MASK 0xffff0000
#define SND_PCM_DEV_NUM_MASK 0xff00
#define SND_PCM_DEV_NUM_MASK 0xff00
@@ -890,12 +891,14 @@ static void uaudio_disconnect_cb(struct snd_usb_audio *chip)
		if (ret < 0)
		if (ret < 0)
			uaudio_err("qmi send failed with err: %d\n", ret);
			uaudio_err("qmi send failed with err: %d\n", ret);


		ret = wait_event_interruptible(dev->disconnect_wq,
		ret = wait_event_interruptible_timeout(dev->disconnect_wq,
				!atomic_read(&dev->in_use));
				!atomic_read(&dev->in_use),
		if (ret < 0) {
				msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT));
			uaudio_dbg("failed with ret %d\n", ret);
		if (!ret)
			return;
			uaudio_err("timeout while waiting for dev_release\n");
		}
		else if (ret < 0)
			uaudio_err("failed with ret %d\n", ret);

		mutex_lock(&chip->dev_lock);
		mutex_lock(&chip->dev_lock);
	}
	}


@@ -1154,14 +1157,18 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,


response:
response:
	if (!req_msg->enable && ret != -EINVAL) {
	if (!req_msg->enable && ret != -EINVAL) {
		if (ret != -ENODEV) {
			if (info_idx >= 0) {
			if (info_idx >= 0) {
				mutex_lock(&chip->dev_lock);
				mutex_lock(&chip->dev_lock);
				info = &uadev[pcm_card_num].info[info_idx];
				info = &uadev[pcm_card_num].info[info_idx];
			uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev, info);
				uaudio_dev_intf_cleanup(
						uadev[pcm_card_num].udev,
						info);
				uaudio_dbg("release resources: intf# %d card# %d\n",
				uaudio_dbg("release resources: intf# %d card# %d\n",
						subs->interface, pcm_card_num);
						subs->interface, pcm_card_num);
				mutex_unlock(&chip->dev_lock);
				mutex_unlock(&chip->dev_lock);
			}
			}
		}
		if (atomic_read(&uadev[pcm_card_num].in_use))
		if (atomic_read(&uadev[pcm_card_num].in_use))
			kref_put(&uadev[pcm_card_num].kref,
			kref_put(&uadev[pcm_card_num].kref,
					uaudio_dev_release);
					uaudio_dev_release);