1 From: Takashi Iwai <tiwai@suse.de>
2 Subject: ALSA: hda - Use own workqueue
3 Patch-mainline: 2.6.29-rc2
6 snd-hda-intel driver used schedule_work() fot the delayed DMA pointer
7 updates, but this has several potential problems:
8 - it may block other eventsd works longer
9 - it may deadlock when probing fails and flush_scheduled_work() is
10 called during probe callback (as probe callback itself could be
13 This patch adds an own workq for each driver instance to solve these
16 Signed-off-by: Takashi Iwai <tiwai@suse.de>
19 sound/pci/hda/hda_beep.c | 1 -
20 sound/pci/hda/hda_codec.c | 24 ++++++++++++++++++------
21 sound/pci/hda/hda_codec.h | 2 ++
22 sound/pci/hda/hda_intel.c | 6 +++---
23 4 files changed, 23 insertions(+), 10 deletions(-)
25 --- a/sound/pci/hda/hda_beep.c
26 +++ b/sound/pci/hda/hda_beep.c
28 struct hda_beep *beep = codec->beep;
30 cancel_work_sync(&beep->beep_work);
31 - flush_scheduled_work();
33 input_unregister_device(beep->dev);
35 --- a/sound/pci/hda/hda_codec.c
36 +++ b/sound/pci/hda/hda_codec.c
38 unsol->queue[wp] = res;
39 unsol->queue[wp + 1] = res_ex;
41 - schedule_work(&unsol->work);
42 + queue_work(bus->workq, &unsol->work);
51 - flush_scheduled_work();
53 + flush_workqueue(bus->workq);
57 list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
58 snd_hda_codec_free(codec);
60 if (bus->ops.private_free)
61 bus->ops.private_free(bus);
63 + destroy_workqueue(bus->workq);
68 mutex_init(&bus->cmd_mutex);
69 INIT_LIST_HEAD(&bus->codec_list);
71 + snprintf(bus->workq_name, sizeof(bus->workq_name),
72 + "hd-audio%d", card->number);
73 + bus->workq = create_singlethread_workqueue(bus->workq_name);
75 + snd_printk(KERN_ERR "cannot create workqueue %s\n",
81 err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
83 snd_hda_bus_free(bus);
86 #ifdef CONFIG_SND_HDA_POWER_SAVE
87 cancel_delayed_work(&codec->power_work);
88 - flush_scheduled_work();
89 + flush_workqueue(codec->bus->workq);
91 list_del(&codec->list);
92 codec->bus->caddr_tbl[codec->addr] = NULL;
96 codec->power_transition = 1; /* avoid reentrance */
97 - schedule_delayed_work(&codec->power_work,
98 + queue_delayed_work(codec->bus->workq, &codec->power_work,
99 msecs_to_jiffies(power_save * 1000));
102 --- a/sound/pci/hda/hda_codec.h
103 +++ b/sound/pci/hda/hda_codec.h
106 /* unsolicited event queue */
107 struct hda_bus_unsolicited *unsol;
108 + char workq_name[16];
109 + struct workqueue_struct *workq; /* common workqueue for codecs */
111 struct snd_info_entry *proc;
113 --- a/sound/pci/hda/hda_intel.c
114 +++ b/sound/pci/hda/hda_intel.c
115 @@ -986,10 +986,11 @@
116 spin_unlock(&chip->reg_lock);
117 snd_pcm_period_elapsed(azx_dev->substream);
118 spin_lock(&chip->reg_lock);
120 + } else if (chip->bus && chip->bus->workq) {
121 /* bogus IRQ, process it later */
122 azx_dev->irq_pending = 1;
123 - schedule_work(&chip->irq_pending_work);
124 + queue_work(chip->bus->workq,
125 + &chip->irq_pending_work);
129 @@ -1708,7 +1709,6 @@
130 for (i = 0; i < chip->num_streams; i++)
131 chip->azx_dev[i].irq_pending = 0;
132 spin_unlock_irq(&chip->reg_lock);
133 - flush_scheduled_work();
136 static struct snd_pcm_ops azx_pcm_ops = {