Commit 59866da9 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Avoid autopm calls after disconnection

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=51201Reviewd-by: default avatarClemens Ladisch <clemens@ladisch.de>
Tested-by: default avatarClemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 467b1035
...@@ -116,6 +116,7 @@ struct snd_usb_midi { ...@@ -116,6 +116,7 @@ struct snd_usb_midi {
struct list_head list; struct list_head list;
struct timer_list error_timer; struct timer_list error_timer;
spinlock_t disc_lock; spinlock_t disc_lock;
struct rw_semaphore disc_rwsem;
struct mutex mutex; struct mutex mutex;
u32 usb_id; u32 usb_id;
int next_midi_device; int next_midi_device;
...@@ -1038,6 +1039,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) ...@@ -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_usb_midi* umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl; struct snd_kcontrol *ctl;
down_read(&umidi->disc_rwsem);
if (umidi->disconnected) {
up_read(&umidi->disc_rwsem);
return;
}
mutex_lock(&umidi->mutex); mutex_lock(&umidi->mutex);
if (open) { if (open) {
if (umidi->opened++ == 0 && umidi->roland_load_ctl) { if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
...@@ -1056,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) ...@@ -1056,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
} }
} }
mutex_unlock(&umidi->mutex); mutex_unlock(&umidi->mutex);
up_read(&umidi->disc_rwsem);
} }
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) 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) ...@@ -1076,8 +1084,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
snd_BUG(); snd_BUG();
return -ENXIO; return -ENXIO;
} }
down_read(&umidi->disc_rwsem);
if (umidi->disconnected) {
up_read(&umidi->disc_rwsem);
return -ENODEV;
}
err = usb_autopm_get_interface(umidi->iface); err = usb_autopm_get_interface(umidi->iface);
port->autopm_reference = err >= 0; port->autopm_reference = err >= 0;
up_read(&umidi->disc_rwsem);
if (err < 0 && err != -EACCES) if (err < 0 && err != -EACCES)
return -EIO; return -EIO;
substream->runtime->private_data = port; substream->runtime->private_data = port;
...@@ -1092,8 +1107,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) ...@@ -1092,8 +1107,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
struct usbmidi_out_port *port = substream->runtime->private_data; struct usbmidi_out_port *port = substream->runtime->private_data;
substream_open(substream, 0); 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); usb_autopm_put_interface(umidi->iface);
up_read(&umidi->disc_rwsem);
return 0; return 0;
} }
...@@ -1403,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p) ...@@ -1403,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p)
* a timer may submit an URB. To reliably break the cycle * a timer may submit an URB. To reliably break the cycle
* a flag under lock must be used * a flag under lock must be used
*/ */
down_write(&umidi->disc_rwsem);
spin_lock_irq(&umidi->disc_lock); spin_lock_irq(&umidi->disc_lock);
umidi->disconnected = 1; umidi->disconnected = 1;
spin_unlock_irq(&umidi->disc_lock); spin_unlock_irq(&umidi->disc_lock);
up_write(&umidi->disc_rwsem);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->out) if (ep->out)
...@@ -2117,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card, ...@@ -2117,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card,
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer); init_timer(&umidi->error_timer);
spin_lock_init(&umidi->disc_lock); spin_lock_init(&umidi->disc_lock);
init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex); mutex_init(&umidi->mutex);
umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
le16_to_cpu(umidi->dev->descriptor.idProduct)); le16_to_cpu(umidi->dev->descriptor.idProduct));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment