]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-own-workq
Reenabled linux-xen, added patches for Xen Kernel Version 2.6.27.31,
[ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.drivers / alsa-post-ga-hda-own-workq
diff --git a/src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-own-workq b/src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-own-workq
new file mode 100644 (file)
index 0000000..8d54b50
--- /dev/null
@@ -0,0 +1,136 @@
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - Use own workqueue
+Patch-mainline: 2.6.29-rc2
+References: bnc#502733
+
+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>
+
+---
+ sound/pci/hda/hda_beep.c  |    1 -
+ sound/pci/hda/hda_codec.c |   24 ++++++++++++++++++------
+ sound/pci/hda/hda_codec.h |    2 ++
+ sound/pci/hda/hda_intel.c |    6 +++---
+ 4 files changed, 23 insertions(+), 10 deletions(-)
+
+--- a/sound/pci/hda/hda_beep.c
++++ b/sound/pci/hda/hda_beep.c
+@@ -134,7 +134,6 @@
+       struct hda_beep *beep = codec->beep;
+       if (beep) {
+               cancel_work_sync(&beep->beep_work);
+-              flush_scheduled_work();
+               input_unregister_device(beep->dev);
+               kfree(beep);
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -310,7 +310,7 @@
+       unsol->queue[wp] = res;
+       unsol->queue[wp + 1] = res_ex;
+-      schedule_work(&unsol->work);
++      queue_work(bus->workq, &unsol->work);
+       return 0;
+ }
+@@ -373,15 +373,17 @@
+       if (!bus)
+               return 0;
+-      if (bus->unsol) {
+-              flush_scheduled_work();
++      if (bus->workq)
++              flush_workqueue(bus->workq);
++      if (bus->unsol)
+               kfree(bus->unsol);
+-      }
+       list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
+               snd_hda_codec_free(codec);
+       }
+       if (bus->ops.private_free)
+               bus->ops.private_free(bus);
++      if (bus->workq)
++              destroy_workqueue(bus->workq);
+       kfree(bus);
+       return 0;
+ }
+@@ -431,6 +433,16 @@
+       mutex_init(&bus->cmd_mutex);
+       INIT_LIST_HEAD(&bus->codec_list);
++      snprintf(bus->workq_name, sizeof(bus->workq_name),
++               "hd-audio%d", card->number);
++      bus->workq = create_singlethread_workqueue(bus->workq_name);
++      if (!bus->workq) {
++              snd_printk(KERN_ERR "cannot create workqueue %s\n",
++                         bus->workq_name);
++              kfree(bus);
++              return -ENOMEM;
++      }
++
+       err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
+       if (err < 0) {
+               snd_hda_bus_free(bus);
+@@ -564,7 +576,7 @@
+               return;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       cancel_delayed_work(&codec->power_work);
+-      flush_scheduled_work();
++      flush_workqueue(codec->bus->workq);
+ #endif
+       list_del(&codec->list);
+       codec->bus->caddr_tbl[codec->addr] = NULL;
+@@ -2533,7 +2545,7 @@
+               return;
+       if (power_save) {
+               codec->power_transition = 1; /* avoid reentrance */
+-              schedule_delayed_work(&codec->power_work,
++              queue_delayed_work(codec->bus->workq, &codec->power_work,
+                                     msecs_to_jiffies(power_save * 1000));
+       }
+ }
+--- a/sound/pci/hda/hda_codec.h
++++ b/sound/pci/hda/hda_codec.h
+@@ -580,6 +580,8 @@
+       /* unsolicited event queue */
+       struct hda_bus_unsolicited *unsol;
++      char workq_name[16];
++      struct workqueue_struct *workq; /* common workqueue for codecs */
+       struct snd_info_entry *proc;
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -986,10 +986,11 @@
+                               spin_unlock(&chip->reg_lock);
+                               snd_pcm_period_elapsed(azx_dev->substream);
+                               spin_lock(&chip->reg_lock);
+-                      } else {
++                      } else if (chip->bus && chip->bus->workq) {
+                               /* bogus IRQ, process it later */
+                               azx_dev->irq_pending = 1;
+-                              schedule_work(&chip->irq_pending_work);
++                              queue_work(chip->bus->workq,
++                                         &chip->irq_pending_work);
+                       }
+               }
+       }
+@@ -1708,7 +1709,6 @@
+       for (i = 0; i < chip->num_streams; i++)
+               chip->azx_dev[i].irq_pending = 0;
+       spin_unlock_irq(&chip->reg_lock);
+-      flush_scheduled_work();
+ }
+ static struct snd_pcm_ops azx_pcm_ops = {