[PATCH v2 30/31] sound/usb: Check media device unregister progress state

From: Shuah Khan
Date: Thu Jan 14 2016 - 14:53:21 EST


Change to release media resources for pcm streams
and mixer before snd_card_disconnect() is done from
usb_audio_disconnect(). The stream and mixer resource
release interfaces access managed media resources
(device resources) created on the usb device parent.
These interfaces should be called before the last
put_device() which releases all the device resources.
In addition, changed the stream and mixer resource
release interfaces to check if media device unregister
is in progress and avoid calling Media Controller API
to unregister entities and remove devnodes. Media device
unregister takes care of all of this. This fixes the
the general protection faults while snd-usb-audio was
cleaning up media resources for pcm streams and mixers.

Signed-off-by: Shuah Khan <shuahkh@xxxxxxxxxxxxxxx>
---
Changes since v1:
- Rebased after patch 26 v2 work.
- No changes to patch.

sound/usb/card.c | 8 ++++++--
sound/usb/media.c | 41 +++++++++++++++++++++++++++++++----------
sound/usb/media.h | 4 ++--
sound/usb/stream.c | 1 -
4 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/sound/usb/card.c b/sound/usb/card.c
index e965982..8959ccb 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -608,6 +608,12 @@ static void usb_audio_disconnect(struct usb_interface *intf)
*/
wait_event(chip->shutdown_wait,
!atomic_read(&chip->usage_count));
+
+ /* release media pcm stream resources */
+ media_stream_delete(chip);
+ /* delete mixer media resources */
+ media_mixer_delete(chip);
+
snd_card_disconnect(card);
/* release the pcm resources */
list_for_each_entry(as, &chip->pcm_list, list) {
@@ -621,8 +627,6 @@ static void usb_audio_disconnect(struct usb_interface *intf)
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
- /* delete mixer media resources */
- media_mixer_delete(chip);
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
diff --git a/sound/usb/media.c b/sound/usb/media.c
index da58378..93c2bdd 100644
--- a/sound/usb/media.c
+++ b/sound/usb/media.c
@@ -77,8 +77,11 @@ void media_device_delete(struct usb_interface *iface)
struct usb_device *usbdev = interface_to_usbdev(iface);

mdev = media_device_find_devres(&usbdev->dev);
- if (mdev && media_devnode_is_registered(&mdev->devnode))
- media_device_unregister(mdev);
+ if (mdev) {
+ if (media_devnode_is_registered(&mdev->devnode) &&
+ !media_device_is_unregister_in_progress(mdev))
+ media_device_unregister(mdev);
+ }
}

static int media_enable_source(struct media_ctl *mctl)
@@ -156,7 +159,7 @@ int media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
return 0;
}

-void media_stream_delete(struct snd_usb_substream *subs)
+static void __media_stream_delete(struct snd_usb_substream *subs)
{
struct media_ctl *mctl = (struct media_ctl *) subs->media_ctl;

@@ -164,7 +167,7 @@ void media_stream_delete(struct snd_usb_substream *subs)
struct media_device *mdev;

mdev = media_device_find_devres(&subs->dev->dev);
- if (mdev) {
+ if (mdev && !media_device_is_unregister_in_progress(mdev)) {
media_devnode_remove(mctl->intf_devnode);
media_device_unregister_entity(&mctl->media_entity);
media_entity_cleanup(&mctl->media_entity);
@@ -174,6 +177,21 @@ void media_stream_delete(struct snd_usb_substream *subs)
}
}

+void media_stream_delete(struct snd_usb_audio *chip)
+{
+ struct snd_usb_stream *as;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ struct snd_usb_substream *subs;
+ int idx;
+
+ for (idx = 0; idx < 2; idx++) {
+ subs = &as->substream[idx];
+ __media_stream_delete(subs);
+ }
+ }
+}
+
int media_start_pipeline(struct snd_usb_substream *subs)
{
struct media_ctl *mctl = (struct media_ctl *) subs->media_ctl;
@@ -262,20 +280,23 @@ void media_mixer_delete(struct snd_usb_audio *chip)
struct media_device *mdev;

mdev = media_device_find_devres(&chip->dev->dev);
- if (!mdev)
- return;

+ if (chip->ctl_intf_media_devnode) {
+ if (mdev && !media_device_is_unregister_in_progress(mdev))
+ media_devnode_remove(chip->ctl_intf_media_devnode);
+ chip->ctl_intf_media_devnode = NULL;
+ }
list_for_each_entry(mixer, &chip->mixer_list, list) {
struct media_mixer_ctl *mctl;

mctl = (struct media_mixer_ctl *) mixer->media_mixer_ctl;
if (!mixer->media_mixer_ctl)
continue;
-
- media_device_unregister_entity(&mctl->media_entity);
- media_entity_cleanup(&mctl->media_entity);
+ if (mdev && !media_device_is_unregister_in_progress(mdev)) {
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ }
kfree(mctl);
mixer->media_mixer_ctl = NULL;
}
- media_devnode_remove(chip->ctl_intf_media_devnode);
}
diff --git a/sound/usb/media.h b/sound/usb/media.h
index 9477ffa..09854da 100644
--- a/sound/usb/media.h
+++ b/sound/usb/media.h
@@ -54,7 +54,7 @@ int media_device_create(struct snd_usb_audio *chip,
void media_device_delete(struct usb_interface *iface);
int media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
int stream);
-void media_stream_delete(struct snd_usb_substream *subs);
+void media_stream_delete(struct snd_usb_audio *chip);
int media_start_pipeline(struct snd_usb_substream *subs);
void media_stop_pipeline(struct snd_usb_substream *subs);
int media_mixer_init(struct snd_usb_audio *chip);
@@ -67,7 +67,7 @@ static inline void media_device_delete(struct usb_interface *iface) { }
static inline int media_stream_init(struct snd_usb_substream *subs,
struct snd_pcm *pcm, int stream)
{ return 0; }
-static inline void media_stream_delete(struct snd_usb_substream *subs) { }
+static inline void media_stream_delete(struct snd_usb_audio *chip) { }
static inline int media_start_pipeline(struct snd_usb_substream *subs)
{ return 0; }
static inline void media_stop_pipeline(struct snd_usb_substream *subs) { }
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 789e515..f96f539 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -53,7 +53,6 @@ static void free_substream(struct snd_usb_substream *subs)
kfree(fp);
}
kfree(subs->rate_list.list);
- media_stream_delete(subs);
}


--
2.5.0