diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-12-03 11:12:46 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-11 09:06:49 -0800 |
commit | 929cfe920d276f8d09b89c83f4ee0c721652d921 (patch) | |
tree | e62d9c9483834ac59fc2ffb269b216a378bd7136 /sound | |
parent | 6718272b115afbf5d80ea1e0a4d18f9fcb32351f (diff) | |
download | lwn-929cfe920d276f8d09b89c83f4ee0c721652d921.tar.gz lwn-929cfe920d276f8d09b89c83f4ee0c721652d921.zip |
ALSA: usb-audio: Avoid autopm calls after disconnection
commit 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993 upstream.
Add a similar protection against the disconnection race and the
invalid use of usb instance after disconnection, as well as we've done
for the USB audio PCM.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=51201
Reviewd-by: Clemens Ladisch <clemens@ladisch.de>
Tested-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/midi.c | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/sound/usb/midi.c b/sound/usb/midi.c index eeefbce3873c..c0054ee9389b 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -116,6 +116,7 @@ struct snd_usb_midi { struct list_head list; struct timer_list error_timer; spinlock_t disc_lock; + struct rw_semaphore disc_rwsem; struct mutex mutex; u32 usb_id; int next_midi_device; @@ -1038,6 +1039,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) struct snd_usb_midi* umidi = substream->rmidi->private_data; struct snd_kcontrol *ctl; + down_read(&umidi->disc_rwsem); + if (umidi->disconnected) { + up_read(&umidi->disc_rwsem); + return; + } + mutex_lock(&umidi->mutex); if (open) { if (umidi->opened++ == 0 && umidi->roland_load_ctl) { @@ -1056,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) } } mutex_unlock(&umidi->mutex); + up_read(&umidi->disc_rwsem); } static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) @@ -1076,8 +1084,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) snd_BUG(); return -ENXIO; } + + down_read(&umidi->disc_rwsem); + if (umidi->disconnected) { + up_read(&umidi->disc_rwsem); + return -ENODEV; + } err = usb_autopm_get_interface(umidi->iface); port->autopm_reference = err >= 0; + up_read(&umidi->disc_rwsem); if (err < 0 && err != -EACCES) return -EIO; substream->runtime->private_data = port; @@ -1092,8 +1107,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) struct usbmidi_out_port *port = substream->runtime->private_data; substream_open(substream, 0); - if (port->autopm_reference) + down_read(&umidi->disc_rwsem); + if (!umidi->disconnected && port->autopm_reference) usb_autopm_put_interface(umidi->iface); + up_read(&umidi->disc_rwsem); return 0; } @@ -1403,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p) * a timer may submit an URB. To reliably break the cycle * a flag under lock must be used */ + down_write(&umidi->disc_rwsem); spin_lock_irq(&umidi->disc_lock); umidi->disconnected = 1; spin_unlock_irq(&umidi->disc_lock); + up_write(&umidi->disc_rwsem); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->out) @@ -2117,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card, umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; init_timer(&umidi->error_timer); spin_lock_init(&umidi->disc_lock); + init_rwsem(&umidi->disc_rwsem); mutex_init(&umidi->mutex); umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); |