diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-12-03 05:12:46 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-12-04 01:27:27 -0500 |
commit | 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993 (patch) | |
tree | 345bf3e8be9d6f0ad9a19c18a531a68ee3dcd55d /sound | |
parent | 467b10350525e53ddeea0b8de5b8b386a830d2a9 (diff) |
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=51201
Reviewd-by: Clemens Ladisch <clemens@ladisch.de>
Tested-by: Clemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
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 { | |||
116 | struct list_head list; | 116 | struct list_head list; |
117 | struct timer_list error_timer; | 117 | struct timer_list error_timer; |
118 | spinlock_t disc_lock; | 118 | spinlock_t disc_lock; |
119 | struct rw_semaphore disc_rwsem; | ||
119 | struct mutex mutex; | 120 | struct mutex mutex; |
120 | u32 usb_id; | 121 | u32 usb_id; |
121 | int next_midi_device; | 122 | int next_midi_device; |
@@ -1038,6 +1039,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) | |||
1038 | struct snd_usb_midi* umidi = substream->rmidi->private_data; | 1039 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1039 | struct snd_kcontrol *ctl; | 1040 | struct snd_kcontrol *ctl; |
1040 | 1041 | ||
1042 | down_read(&umidi->disc_rwsem); | ||
1043 | if (umidi->disconnected) { | ||
1044 | up_read(&umidi->disc_rwsem); | ||
1045 | return; | ||
1046 | } | ||
1047 | |||
1041 | mutex_lock(&umidi->mutex); | 1048 | mutex_lock(&umidi->mutex); |
1042 | if (open) { | 1049 | if (open) { |
1043 | if (umidi->opened++ == 0 && umidi->roland_load_ctl) { | 1050 | if (umidi->opened++ == 0 && umidi->roland_load_ctl) { |
@@ -1056,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open) | |||
1056 | } | 1063 | } |
1057 | } | 1064 | } |
1058 | mutex_unlock(&umidi->mutex); | 1065 | mutex_unlock(&umidi->mutex); |
1066 | up_read(&umidi->disc_rwsem); | ||
1059 | } | 1067 | } |
1060 | 1068 | ||
1061 | static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) | 1069 | 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 | snd_BUG(); | 1084 | snd_BUG(); |
1077 | return -ENXIO; | 1085 | return -ENXIO; |
1078 | } | 1086 | } |
1087 | |||
1088 | down_read(&umidi->disc_rwsem); | ||
1089 | if (umidi->disconnected) { | ||
1090 | up_read(&umidi->disc_rwsem); | ||
1091 | return -ENODEV; | ||
1092 | } | ||
1079 | err = usb_autopm_get_interface(umidi->iface); | 1093 | err = usb_autopm_get_interface(umidi->iface); |
1080 | port->autopm_reference = err >= 0; | 1094 | port->autopm_reference = err >= 0; |
1095 | up_read(&umidi->disc_rwsem); | ||
1081 | if (err < 0 && err != -EACCES) | 1096 | if (err < 0 && err != -EACCES) |
1082 | return -EIO; | 1097 | return -EIO; |
1083 | substream->runtime->private_data = port; | 1098 | substream->runtime->private_data = port; |
@@ -1092,8 +1107,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) | |||
1092 | struct usbmidi_out_port *port = substream->runtime->private_data; | 1107 | struct usbmidi_out_port *port = substream->runtime->private_data; |
1093 | 1108 | ||
1094 | substream_open(substream, 0); | 1109 | substream_open(substream, 0); |
1095 | if (port->autopm_reference) | 1110 | down_read(&umidi->disc_rwsem); |
1111 | if (!umidi->disconnected && port->autopm_reference) | ||
1096 | usb_autopm_put_interface(umidi->iface); | 1112 | usb_autopm_put_interface(umidi->iface); |
1113 | up_read(&umidi->disc_rwsem); | ||
1097 | return 0; | 1114 | return 0; |
1098 | } | 1115 | } |
1099 | 1116 | ||
@@ -1403,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p) | |||
1403 | * a timer may submit an URB. To reliably break the cycle | 1420 | * a timer may submit an URB. To reliably break the cycle |
1404 | * a flag under lock must be used | 1421 | * a flag under lock must be used |
1405 | */ | 1422 | */ |
1423 | down_write(&umidi->disc_rwsem); | ||
1406 | spin_lock_irq(&umidi->disc_lock); | 1424 | spin_lock_irq(&umidi->disc_lock); |
1407 | umidi->disconnected = 1; | 1425 | umidi->disconnected = 1; |
1408 | spin_unlock_irq(&umidi->disc_lock); | 1426 | spin_unlock_irq(&umidi->disc_lock); |
1427 | up_write(&umidi->disc_rwsem); | ||
1428 | |||
1409 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { | 1429 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { |
1410 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; | 1430 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; |
1411 | if (ep->out) | 1431 | if (ep->out) |
@@ -2117,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card, | |||
2117 | umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; | 2137 | umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; |
2118 | init_timer(&umidi->error_timer); | 2138 | init_timer(&umidi->error_timer); |
2119 | spin_lock_init(&umidi->disc_lock); | 2139 | spin_lock_init(&umidi->disc_lock); |
2140 | init_rwsem(&umidi->disc_rwsem); | ||
2120 | mutex_init(&umidi->mutex); | 2141 | mutex_init(&umidi->mutex); |
2121 | umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), | 2142 | umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), |
2122 | le16_to_cpu(umidi->dev->descriptor.idProduct)); | 2143 | le16_to_cpu(umidi->dev->descriptor.idProduct)); |