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 | |
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')
-rw-r--r-- | sound/pci/hda/hda_beep.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 23 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 6 |
4 files changed, 21 insertions, 10 deletions
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e00421c0d8ba..960fd7970384 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c | |||
@@ -135,7 +135,6 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) | |||
135 | struct hda_beep *beep = codec->beep; | 135 | struct hda_beep *beep = codec->beep; |
136 | if (beep) { | 136 | if (beep) { |
137 | cancel_work_sync(&beep->beep_work); | 137 | cancel_work_sync(&beep->beep_work); |
138 | flush_scheduled_work(); | ||
139 | 138 | ||
140 | input_unregister_device(beep->dev); | 139 | input_unregister_device(beep->dev); |
141 | kfree(beep); | 140 | kfree(beep); |
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 */ |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index e9c723e13e5d..5810ef588402 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -614,6 +614,7 @@ struct hda_bus { | |||
614 | 614 | ||
615 | /* unsolicited event queue */ | 615 | /* unsolicited event queue */ |
616 | struct hda_bus_unsolicited *unsol; | 616 | struct hda_bus_unsolicited *unsol; |
617 | struct workqueue_struct *workq; /* common workqueue for codecs */ | ||
617 | 618 | ||
618 | /* assigned PCMs */ | 619 | /* assigned PCMs */ |
619 | DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); | 620 | DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); |
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f04de115ee11..11e791b965f6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c | |||
@@ -996,10 +996,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) | |||
996 | spin_unlock(&chip->reg_lock); | 996 | spin_unlock(&chip->reg_lock); |
997 | snd_pcm_period_elapsed(azx_dev->substream); | 997 | snd_pcm_period_elapsed(azx_dev->substream); |
998 | spin_lock(&chip->reg_lock); | 998 | spin_lock(&chip->reg_lock); |
999 | } else { | 999 | } else if (chip->bus && chip->bus->workq) { |
1000 | /* bogus IRQ, process it later */ | 1000 | /* bogus IRQ, process it later */ |
1001 | azx_dev->irq_pending = 1; | 1001 | azx_dev->irq_pending = 1; |
1002 | schedule_work(&chip->irq_pending_work); | 1002 | queue_work(chip->bus->workq, |
1003 | &chip->irq_pending_work); | ||
1003 | } | 1004 | } |
1004 | } | 1005 | } |
1005 | } | 1006 | } |
@@ -1741,7 +1742,6 @@ static void azx_clear_irq_pending(struct azx *chip) | |||
1741 | for (i = 0; i < chip->num_streams; i++) | 1742 | for (i = 0; i < chip->num_streams; i++) |
1742 | chip->azx_dev[i].irq_pending = 0; | 1743 | chip->azx_dev[i].irq_pending = 0; |
1743 | spin_unlock_irq(&chip->reg_lock); | 1744 | spin_unlock_irq(&chip->reg_lock); |
1744 | flush_scheduled_work(); | ||
1745 | } | 1745 | } |
1746 | 1746 | ||
1747 | static struct snd_pcm_ops azx_pcm_ops = { | 1747 | static struct snd_pcm_ops azx_pcm_ops = { |