]>
Commit | Line | Data |
---|---|---|
1 | From: Takashi Iwai <tiwai@suse.de> | |
2 | Subject: ALSA: hda - Use own workqueue | |
3 | Patch-mainline: 2.6.29-rc2 | |
4 | References: bnc#502733 | |
5 | ||
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 | |
11 | invoked from eventd) | |
12 | ||
13 | This patch adds an own workq for each driver instance to solve these | |
14 | problems. | |
15 | ||
16 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
17 | ||
18 | --- | |
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(-) | |
24 | ||
25 | --- a/sound/pci/hda/hda_beep.c | |
26 | +++ b/sound/pci/hda/hda_beep.c | |
27 | @@ -134,7 +134,6 @@ | |
28 | struct hda_beep *beep = codec->beep; | |
29 | if (beep) { | |
30 | cancel_work_sync(&beep->beep_work); | |
31 | - flush_scheduled_work(); | |
32 | ||
33 | input_unregister_device(beep->dev); | |
34 | kfree(beep); | |
35 | --- a/sound/pci/hda/hda_codec.c | |
36 | +++ b/sound/pci/hda/hda_codec.c | |
37 | @@ -310,7 +310,7 @@ | |
38 | unsol->queue[wp] = res; | |
39 | unsol->queue[wp + 1] = res_ex; | |
40 | ||
41 | - schedule_work(&unsol->work); | |
42 | + queue_work(bus->workq, &unsol->work); | |
43 | ||
44 | return 0; | |
45 | } | |
46 | @@ -373,15 +373,17 @@ | |
47 | ||
48 | if (!bus) | |
49 | return 0; | |
50 | - if (bus->unsol) { | |
51 | - flush_scheduled_work(); | |
52 | + if (bus->workq) | |
53 | + flush_workqueue(bus->workq); | |
54 | + if (bus->unsol) | |
55 | kfree(bus->unsol); | |
56 | - } | |
57 | list_for_each_entry_safe(codec, n, &bus->codec_list, list) { | |
58 | snd_hda_codec_free(codec); | |
59 | } | |
60 | if (bus->ops.private_free) | |
61 | bus->ops.private_free(bus); | |
62 | + if (bus->workq) | |
63 | + destroy_workqueue(bus->workq); | |
64 | kfree(bus); | |
65 | return 0; | |
66 | } | |
67 | @@ -431,6 +433,16 @@ | |
68 | mutex_init(&bus->cmd_mutex); | |
69 | INIT_LIST_HEAD(&bus->codec_list); | |
70 | ||
71 | + snprintf(bus->workq_name, sizeof(bus->workq_name), | |
72 | + "hd-audio%d", card->number); | |
73 | + bus->workq = create_singlethread_workqueue(bus->workq_name); | |
74 | + if (!bus->workq) { | |
75 | + snd_printk(KERN_ERR "cannot create workqueue %s\n", | |
76 | + bus->workq_name); | |
77 | + kfree(bus); | |
78 | + return -ENOMEM; | |
79 | + } | |
80 | + | |
81 | err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); | |
82 | if (err < 0) { | |
83 | snd_hda_bus_free(bus); | |
84 | @@ -564,7 +576,7 @@ | |
85 | return; | |
86 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
87 | cancel_delayed_work(&codec->power_work); | |
88 | - flush_scheduled_work(); | |
89 | + flush_workqueue(codec->bus->workq); | |
90 | #endif | |
91 | list_del(&codec->list); | |
92 | codec->bus->caddr_tbl[codec->addr] = NULL; | |
93 | @@ -2533,7 +2545,7 @@ | |
94 | return; | |
95 | if (power_save) { | |
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)); | |
100 | } | |
101 | } | |
102 | --- a/sound/pci/hda/hda_codec.h | |
103 | +++ b/sound/pci/hda/hda_codec.h | |
104 | @@ -580,6 +580,8 @@ | |
105 | ||
106 | /* unsolicited event queue */ | |
107 | struct hda_bus_unsolicited *unsol; | |
108 | + char workq_name[16]; | |
109 | + struct workqueue_struct *workq; /* common workqueue for codecs */ | |
110 | ||
111 | struct snd_info_entry *proc; | |
112 | ||
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); | |
119 | - } else { | |
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); | |
126 | } | |
127 | } | |
128 | } | |
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(); | |
134 | } | |
135 | ||
136 | static struct snd_pcm_ops azx_pcm_ops = { |