Skip to content

Commit 47ab154

Browse files
committed
ALSA: usb-audio: Avoid nested autoresume calls
After the recent fix of runtime PM for USB-audio driver, we got a lockdep warning like: ============================================= [ INFO: possible recursive locking detected ] 4.2.0-rc8+ #61 Not tainted --------------------------------------------- pulseaudio/980 is trying to acquire lock: (&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio] but task is already holding lock: (&chip->shutdown_rwsem){.+.+.+}, at: [<ffffffffa0355dac>] snd_usb_autoresume+0x1d/0x52 [snd_usb_audio] This comes from snd_usb_autoresume() invoking down_read() and it's used in a nested way. Although it's basically safe, per se (as these are read locks), it's better to reduce such spurious warnings. The read lock is needed to guarantee the execution of "shutdown" (cleanup at disconnection) task after all concurrent tasks are finished. This can be implemented in another better way. Also, the current check of chip->in_pm isn't good enough for protecting the racy execution of multiple auto-resumes. This patch rewrites the logic of snd_usb_autoresume() & co; namely, - The recursive call of autopm is avoided by the new refcount, chip->active. The chip->in_pm flag is removed accordingly. - Instead of rwsem, another refcount, chip->usage_count, is introduced for tracking the period to delay the shutdown procedure. At the last clear of this refcount, wake_up() to the shutdown waiter is called. - The shutdown flag is replaced with shutdown atomic count; this is for reducing the lock. - Two new helpers are introduced to simplify the management of these refcounts; snd_usb_lock_shutdown() increases the usage_count, checks the shutdown state, and does autoresume. snd_usb_unlock_shutdown() does the opposite. Most of mixer and other codes just need this, and simply returns an error if it receives an error from lock. Fixes: 9003ebb ('ALSA: usb-audio: Fix runtime PM unbalance') Reported-and-tested-by: Alexnader Kuleshov <kuleshovmail@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent b25cf30 commit 47ab154

7 files changed

Lines changed: 145 additions & 143 deletions

File tree

sound/usb/card.c

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,15 @@ static int snd_usb_audio_create(struct usb_interface *intf,
365365
}
366366

367367
mutex_init(&chip->mutex);
368-
init_rwsem(&chip->shutdown_rwsem);
368+
init_waitqueue_head(&chip->shutdown_wait);
369369
chip->index = idx;
370370
chip->dev = dev;
371371
chip->card = card;
372372
chip->setup = device_setup[idx];
373373
chip->autoclock = autoclock;
374374
chip->probing = 1;
375+
atomic_set(&chip->usage_count, 0);
376+
atomic_set(&chip->shutdown, 0);
375377

376378
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
377379
le16_to_cpu(dev->descriptor.idProduct));
@@ -495,7 +497,7 @@ static int usb_audio_probe(struct usb_interface *intf,
495497
mutex_lock(&register_mutex);
496498
for (i = 0; i < SNDRV_CARDS; i++) {
497499
if (usb_chip[i] && usb_chip[i]->dev == dev) {
498-
if (usb_chip[i]->shutdown) {
500+
if (atomic_read(&usb_chip[i]->shutdown)) {
499501
dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n");
500502
err = -EIO;
501503
goto __error;
@@ -585,23 +587,23 @@ static void usb_audio_disconnect(struct usb_interface *intf)
585587
struct snd_usb_audio *chip = usb_get_intfdata(intf);
586588
struct snd_card *card;
587589
struct list_head *p;
588-
bool was_shutdown;
589590

590591
if (chip == (void *)-1L)
591592
return;
592593

593594
card = chip->card;
594-
down_write(&chip->shutdown_rwsem);
595-
was_shutdown = chip->shutdown;
596-
chip->shutdown = 1;
597-
up_write(&chip->shutdown_rwsem);
598595

599596
mutex_lock(&register_mutex);
600-
if (!was_shutdown) {
597+
if (atomic_inc_return(&chip->shutdown) == 1) {
601598
struct snd_usb_stream *as;
602599
struct snd_usb_endpoint *ep;
603600
struct usb_mixer_interface *mixer;
604601

602+
/* wait until all pending tasks done;
603+
* they are protected by snd_usb_lock_shutdown()
604+
*/
605+
wait_event(chip->shutdown_wait,
606+
!atomic_read(&chip->usage_count));
605607
snd_card_disconnect(card);
606608
/* release the pcm resources */
607609
list_for_each_entry(as, &chip->pcm_list, list) {
@@ -631,28 +633,54 @@ static void usb_audio_disconnect(struct usb_interface *intf)
631633
}
632634
}
633635

634-
#ifdef CONFIG_PM
635-
636-
int snd_usb_autoresume(struct snd_usb_audio *chip)
636+
/* lock the shutdown (disconnect) task and autoresume */
637+
int snd_usb_lock_shutdown(struct snd_usb_audio *chip)
637638
{
638-
int err = -ENODEV;
639+
int err;
639640

640-
down_read(&chip->shutdown_rwsem);
641-
if (chip->probing || chip->in_pm)
642-
err = 0;
643-
else if (!chip->shutdown)
644-
err = usb_autopm_get_interface(chip->pm_intf);
645-
up_read(&chip->shutdown_rwsem);
641+
atomic_inc(&chip->usage_count);
642+
if (atomic_read(&chip->shutdown)) {
643+
err = -EIO;
644+
goto error;
645+
}
646+
err = snd_usb_autoresume(chip);
647+
if (err < 0)
648+
goto error;
649+
return 0;
646650

651+
error:
652+
if (atomic_dec_and_test(&chip->usage_count))
653+
wake_up(&chip->shutdown_wait);
647654
return err;
648655
}
649656

657+
/* autosuspend and unlock the shutdown */
658+
void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
659+
{
660+
snd_usb_autosuspend(chip);
661+
if (atomic_dec_and_test(&chip->usage_count))
662+
wake_up(&chip->shutdown_wait);
663+
}
664+
665+
#ifdef CONFIG_PM
666+
667+
int snd_usb_autoresume(struct snd_usb_audio *chip)
668+
{
669+
if (atomic_read(&chip->shutdown))
670+
return -EIO;
671+
if (chip->probing)
672+
return 0;
673+
if (atomic_inc_return(&chip->active) == 1)
674+
return usb_autopm_get_interface(chip->pm_intf);
675+
return 0;
676+
}
677+
650678
void snd_usb_autosuspend(struct snd_usb_audio *chip)
651679
{
652-
down_read(&chip->shutdown_rwsem);
653-
if (!chip->shutdown && !chip->probing && !chip->in_pm)
680+
if (chip->probing)
681+
return;
682+
if (atomic_dec_and_test(&chip->active))
654683
usb_autopm_put_interface(chip->pm_intf);
655-
up_read(&chip->shutdown_rwsem);
656684
}
657685

658686
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
@@ -705,7 +733,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
705733
if (--chip->num_suspended_intf)
706734
return 0;
707735

708-
chip->in_pm = 1;
736+
atomic_inc(&chip->active); /* avoid autopm */
709737
/*
710738
* ALSA leaves material resumption to user space
711739
* we just notify and restart the mixers
@@ -725,7 +753,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
725753
chip->autosuspended = 0;
726754

727755
err_out:
728-
chip->in_pm = 0;
756+
atomic_dec(&chip->active); /* allow autopm after this point */
729757
return err;
730758
}
731759

sound/usb/endpoint.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,10 @@ static void snd_complete_urb(struct urb *urb)
355355
if (unlikely(urb->status == -ENOENT || /* unlinked */
356356
urb->status == -ENODEV || /* device removed */
357357
urb->status == -ECONNRESET || /* unlinked */
358-
urb->status == -ESHUTDOWN || /* device disabled */
359-
ep->chip->shutdown)) /* device disconnected */
358+
urb->status == -ESHUTDOWN)) /* device disabled */
359+
goto exit_clear;
360+
/* device disconnected */
361+
if (unlikely(atomic_read(&ep->chip->shutdown)))
360362
goto exit_clear;
361363

362364
if (usb_pipeout(ep->pipe)) {
@@ -529,7 +531,7 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force)
529531
{
530532
unsigned int i;
531533

532-
if (!force && ep->chip->shutdown) /* to be sure... */
534+
if (!force && atomic_read(&ep->chip->shutdown)) /* to be sure... */
533535
return -EBADFD;
534536

535537
clear_bit(EP_FLAG_RUNNING, &ep->flags);
@@ -868,7 +870,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
868870
int err;
869871
unsigned int i;
870872

871-
if (ep->chip->shutdown)
873+
if (atomic_read(&ep->chip->shutdown))
872874
return -EBADFD;
873875

874876
/* already running? */

sound/usb/mixer.c

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,11 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
311311
int timeout = 10;
312312
int idx = 0, err;
313313

314-
err = snd_usb_autoresume(chip);
314+
err = snd_usb_lock_shutdown(chip);
315315
if (err < 0)
316316
return -EIO;
317317

318-
down_read(&chip->shutdown_rwsem);
319318
while (timeout-- > 0) {
320-
if (chip->shutdown)
321-
break;
322319
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
323320
if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
324321
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
@@ -334,8 +331,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
334331
err = -EINVAL;
335332

336333
out:
337-
up_read(&chip->shutdown_rwsem);
338-
snd_usb_autosuspend(chip);
334+
snd_usb_unlock_shutdown(chip);
339335
return err;
340336
}
341337

@@ -358,21 +354,15 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
358354

359355
memset(buf, 0, sizeof(buf));
360356

361-
ret = snd_usb_autoresume(chip) ? -EIO : 0;
357+
ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
362358
if (ret)
363359
goto error;
364360

365-
down_read(&chip->shutdown_rwsem);
366-
if (chip->shutdown) {
367-
ret = -ENODEV;
368-
} else {
369-
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
370-
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
361+
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
362+
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
371363
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
372364
validx, idx, buf, size);
373-
}
374-
up_read(&chip->shutdown_rwsem);
375-
snd_usb_autosuspend(chip);
365+
snd_usb_unlock_shutdown(chip);
376366

377367
if (ret < 0) {
378368
error:
@@ -485,13 +475,12 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
485475
buf[1] = (value_set >> 8) & 0xff;
486476
buf[2] = (value_set >> 16) & 0xff;
487477
buf[3] = (value_set >> 24) & 0xff;
488-
err = snd_usb_autoresume(chip);
478+
479+
err = snd_usb_lock_shutdown(chip);
489480
if (err < 0)
490481
return -EIO;
491-
down_read(&chip->shutdown_rwsem);
482+
492483
while (timeout-- > 0) {
493-
if (chip->shutdown)
494-
break;
495484
idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
496485
if (snd_usb_ctl_msg(chip->dev,
497486
usb_sndctrlpipe(chip->dev, 0), request,
@@ -506,8 +495,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
506495
err = -EINVAL;
507496

508497
out:
509-
up_read(&chip->shutdown_rwsem);
510-
snd_usb_autosuspend(chip);
498+
snd_usb_unlock_shutdown(chip);
511499
return err;
512500
}
513501

0 commit comments

Comments
 (0)