diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-01-12 04:09:24 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-01-12 04:33:56 -0500 |
commit | 6acaed38a32e8571e92cfc832b971f9e4450c207 (patch) | |
tree | a3d4bff13d946744b38dce3f48ad530d23e74bf7 /sound/pci/hda/hda_codec.c | |
parent | 4b558991049c12689e5fd645222864b8a80730f1 (diff) |
ALSA: hda - Use own workqueue
snd-hda-intel driver used schedule_work() fot the delayed DMA pointer
updates, but this has several potential problems:
- it may block other eventsd works longer
- it may deadlock when probing fails and flush_scheduled_work() is
called during probe callback (as probe callback itself could be
invoked from eventd)
This patch adds an own workq for each driver instance to solve these
problems.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f80e5f3b97dc..3c596da2b9b5 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -373,7 +373,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) | |||
373 | unsol->queue[wp] = res; | 373 | unsol->queue[wp] = res; |
374 | unsol->queue[wp + 1] = res_ex; | 374 | unsol->queue[wp + 1] = res_ex; |
375 | 375 | ||
376 | schedule_work(&unsol->work); | 376 | queue_work(bus->workq, &unsol->work); |
377 | 377 | ||
378 | return 0; | 378 | return 0; |
379 | } | 379 | } |
@@ -437,15 +437,17 @@ static int snd_hda_bus_free(struct hda_bus *bus) | |||
437 | 437 | ||
438 | if (!bus) | 438 | if (!bus) |
439 | return 0; | 439 | return 0; |
440 | if (bus->unsol) { | 440 | if (bus->workq) |
441 | flush_scheduled_work(); | 441 | flush_workqueue(bus->workq); |
442 | if (bus->unsol) | ||
442 | kfree(bus->unsol); | 443 | kfree(bus->unsol); |
443 | } | ||
444 | list_for_each_entry_safe(codec, n, &bus->codec_list, list) { | 444 | list_for_each_entry_safe(codec, n, &bus->codec_list, list) { |
445 | snd_hda_codec_free(codec); | 445 | snd_hda_codec_free(codec); |
446 | } | 446 | } |
447 | if (bus->ops.private_free) | 447 | if (bus->ops.private_free) |
448 | bus->ops.private_free(bus); | 448 | bus->ops.private_free(bus); |
449 | if (bus->workq) | ||
450 | destroy_workqueue(bus->workq); | ||
449 | kfree(bus); | 451 | kfree(bus); |
450 | return 0; | 452 | return 0; |
451 | } | 453 | } |
@@ -485,6 +487,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, | |||
485 | { | 487 | { |
486 | struct hda_bus *bus; | 488 | struct hda_bus *bus; |
487 | int err; | 489 | int err; |
490 | char qname[8]; | ||
488 | static struct snd_device_ops dev_ops = { | 491 | static struct snd_device_ops dev_ops = { |
489 | .dev_register = snd_hda_bus_dev_register, | 492 | .dev_register = snd_hda_bus_dev_register, |
490 | .dev_free = snd_hda_bus_dev_free, | 493 | .dev_free = snd_hda_bus_dev_free, |
@@ -514,6 +517,14 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, | |||
514 | mutex_init(&bus->cmd_mutex); | 517 | mutex_init(&bus->cmd_mutex); |
515 | INIT_LIST_HEAD(&bus->codec_list); | 518 | INIT_LIST_HEAD(&bus->codec_list); |
516 | 519 | ||
520 | snprintf(qname, sizeof(qname), "hda%d", card->number); | ||
521 | bus->workq = create_workqueue(qname); | ||
522 | if (!bus->workq) { | ||
523 | snd_printk(KERN_ERR "cannot create workqueue %s\n", qname); | ||
524 | kfree(bus); | ||
525 | return -ENOMEM; | ||
526 | } | ||
527 | |||
517 | err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); | 528 | err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); |
518 | if (err < 0) { | 529 | if (err < 0) { |
519 | snd_hda_bus_free(bus); | 530 | snd_hda_bus_free(bus); |
@@ -684,7 +695,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) | |||
684 | return; | 695 | return; |
685 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 696 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
686 | cancel_delayed_work(&codec->power_work); | 697 | cancel_delayed_work(&codec->power_work); |
687 | flush_scheduled_work(); | 698 | flush_workqueue(codec->bus->workq); |
688 | #endif | 699 | #endif |
689 | list_del(&codec->list); | 700 | list_del(&codec->list); |
690 | snd_array_free(&codec->mixers); | 701 | snd_array_free(&codec->mixers); |
@@ -1273,7 +1284,7 @@ void snd_hda_codec_reset(struct hda_codec *codec) | |||
1273 | 1284 | ||
1274 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1285 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1275 | cancel_delayed_work(&codec->power_work); | 1286 | cancel_delayed_work(&codec->power_work); |
1276 | flush_scheduled_work(); | 1287 | flush_workqueue(codec->bus->workq); |
1277 | #endif | 1288 | #endif |
1278 | snd_hda_ctls_clear(codec); | 1289 | snd_hda_ctls_clear(codec); |
1279 | /* relase PCMs */ | 1290 | /* relase PCMs */ |