From db8948e653e12b218058bb6696f4a33fa7845f64 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2016 09:17:30 +0100 Subject: [PATCH 01/11] ALSA: hda - Fix bass pin fixup for ASUS N550JX ASUS N550JX (PCI SSID 1043:13df) requires the same fixup for a bass speaker output pin as other N550 models. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=110001 Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8143c0e24a27..33753244f48f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6566,6 +6566,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A), + SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), From 2ba1fe7a06d3624f9a7586d672b55f08f7c670f3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2016 13:52:47 +0100 Subject: [PATCH 02/11] ALSA: hrtimer: Fix stall by hrtimer_cancel() hrtimer_cancel() waits for the completion from the callback, thus it must not be called inside the callback itself. This was already a problem in the past with ALSA hrtimer driver, and the early commit [fcfdebe70759: ALSA: hrtimer - Fix lock-up] tried to address it. However, the previous fix is still insufficient: it may still cause a lockup when the ALSA timer instance reprograms itself in its callback. Then it invokes the start function even in snd_timer_interrupt() that is called in hrtimer callback itself, results in a CPU stall. This is no hypothetical problem but actually triggered by syzkaller fuzzer. This patch tries to fix the issue again. Now we call hrtimer_try_to_cancel() at both start and stop functions so that it won't fall into a deadlock, yet giving some chance to cancel the queue if the functions have been called outside the callback. The proper hrtimer_cancel() is called in anyway at closing, so this should be enough. Reported-and-tested-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/hrtimer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index f845ecf7e172..656d9a9032dc 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t) struct snd_hrtimer *stime = t->private_data; atomic_set(&stime->running, 0); - hrtimer_cancel(&stime->hrt); + hrtimer_try_to_cancel(&stime->hrt); hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), HRTIMER_MODE_REL); atomic_set(&stime->running, 1); @@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; atomic_set(&stime->running, 0); + hrtimer_try_to_cancel(&stime->hrt); return 0; } From 43c54b8c7cfe22f868a751ba8a59abf1724160b1 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 18 Jan 2016 21:35:00 +0800 Subject: [PATCH 03/11] ALSA: pcm: Fix snd_pcm_hw_params struct copy in compat mode This reverts one hunk of commit ef44a1ec6eee ("ALSA: sound/core: use memdup_user()"), which replaced a number of kmalloc followed by memcpy with memdup calls. In this case, we are copying from a struct snd_pcm_hw_params32 to a struct snd_pcm_hw_params, but the latter is 4 bytes longer than the 32-bit version, so we need to separate kmalloc and copy calls. This actually leads to an out-of-bounds memory access later on in sound/soc/soc-pcm.c:soc_pcm_hw_params() (detected using KASan). Fixes: ef44a1ec6eee ('ALSA: sound/core: use memdup_user()') Signed-off-by: Nicolas Boichat Cc: Signed-off-by: Takashi Iwai --- sound/core/pcm_compat.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index b48b434444ed..9630e9f72b7b 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -255,10 +255,15 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, if (! (runtime = substream->runtime)) return -ENOTTY; - /* only fifo_size is different, so just copy all */ - data = memdup_user(data32, sizeof(*data32)); - if (IS_ERR(data)) - return PTR_ERR(data); + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* only fifo_size (RO from userspace) is different, so just copy all */ + if (copy_from_user(data, data32, sizeof(*data32))) { + err = -EFAULT; + goto error; + } if (refine) err = snd_pcm_hw_refine(substream, data); From 9586495dc3011a80602329094e746dbce16cb1f1 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 18 Jan 2016 21:35:01 +0800 Subject: [PATCH 04/11] ALSA: seq: Fix snd_seq_call_port_info_ioctl in compat mode This reverts one hunk of commit ef44a1ec6eee ("ALSA: sound/core: use memdup_user()"), which replaced a number of kmalloc followed by memcpy with memdup calls. In this case, we are copying from a struct snd_seq_port_info32 to a struct snd_seq_port_info, but the latter is 4 bytes longer than the 32-bit version, so we need to separate kmalloc and copy calls. Fixes: ef44a1ec6eee ('ALSA: sound/core: use memdup_user()') Signed-off-by: Nicolas Boichat Cc: Signed-off-by: Takashi Iwai --- sound/core/seq/seq_compat.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 81f7c109dc46..65175902a68a 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -49,11 +49,12 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned struct snd_seq_port_info *data; mm_segment_t fs; - data = memdup_user(data32, sizeof(*data32)); - if (IS_ERR(data)) - return PTR_ERR(data); + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; - if (get_user(data->flags, &data32->flags) || + if (copy_from_user(data, data32, sizeof(*data32)) || + get_user(data->flags, &data32->flags) || get_user(data->time_queue, &data32->time_queue)) goto error; data->kernel = NULL; From c0bcdbdff3ff73a54161fca3cb8b6cdbd0bb8762 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2016 14:12:40 +0100 Subject: [PATCH 05/11] ALSA: control: Avoid kernel warnings from tlv ioctl with numid 0 When a TLV ioctl with numid zero is handled, the driver may spew a kernel warning with a stack trace at each call. The check was intended obviously only for a kernel driver, but not for a user interaction. Let's fix it. This was spotted by syzkaller fuzzer. Reported-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/control.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/control.c b/sound/core/control.c index 196a6fe100ca..a85d45595d02 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1405,6 +1405,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, return -EFAULT; if (tlv.length < sizeof(unsigned int) * 2) return -EINVAL; + if (!tlv.numid) + return -EINVAL; down_read(&card->controls_rwsem); kctl = snd_ctl_find_numid(card, tlv.numid); if (kctl == NULL) { From 3568459a51136f418ca8cad48a0db3803010148a Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Sun, 17 Jan 2016 22:31:02 +0000 Subject: [PATCH 06/11] ALSA: at73c213: manage SSC clock Since commit 49af54ff0fc7 ("misc: atmel-ssc: prepare clock only when request") the SSC driver expects clients to manage the SSC clock. This updates the at73c213 driver to enable the SSC clock whenever it is needed. Signed-off-by: Mans Rullgard Signed-off-by: Takashi Iwai --- sound/spi/at73c213.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 39522367897c..fac7e6eb9529 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -221,6 +221,8 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) runtime->hw = snd_at73c213_playback_hw; chip->substream = substream; + clk_enable(chip->ssc->clk); + return 0; } @@ -228,6 +230,7 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) { struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); chip->substream = NULL; + clk_disable(chip->ssc->clk); return 0; } @@ -897,6 +900,8 @@ static int snd_at73c213_dev_init(struct snd_card *card, chip->card = card; chip->irq = -1; + clk_enable(chip->ssc->clk); + retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); if (retval) { dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq); @@ -935,6 +940,8 @@ static int snd_at73c213_dev_init(struct snd_card *card, free_irq(chip->irq, chip); chip->irq = -1; out: + clk_disable(chip->ssc->clk); + return retval; } @@ -1012,7 +1019,9 @@ static int snd_at73c213_remove(struct spi_device *spi) int retval; /* Stop playback. */ + clk_enable(chip->ssc->clk); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); + clk_disable(chip->ssc->clk); /* Mute sound. */ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f); @@ -1080,6 +1089,7 @@ static int snd_at73c213_suspend(struct device *dev) struct snd_at73c213 *chip = card->private_data; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); + clk_disable(chip->ssc->clk); clk_disable(chip->board->dac_clk); return 0; @@ -1091,6 +1101,7 @@ static int snd_at73c213_resume(struct device *dev) struct snd_at73c213 *chip = card->private_data; clk_enable(chip->board->dac_clk); + clk_enable(chip->ssc->clk); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); return 0; From bed2e98e1f4db8b827df507abc30be7b11b0613d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 Jan 2016 15:00:26 +0100 Subject: [PATCH 07/11] ALSA: hda - Degrade i915 binding failure message Currently HD-audio driver on Intel Skylake or Broxteon gives an error message when binding with i915 audio component fails. However, this isn't any serious error on a system without Intel graphics. Indeed there are such systems, where a third-party codec (e.g. Creative) is put on the mobo while using other discrete GPU (e.g. Nvidia). Printing a kernel "error" message is overreaction in such a case. This patch downgrades the print level for that message. For systems that mandate the i915 binding (e.g. Haswell or Broadwell HDMI/DP), another kernel error message is shown in addition to make clear what went wrong. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111021 Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 2 +- sound/pci/hda/hda_intel.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index c50177fb469f..f6854dbd7d8d 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -306,7 +306,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus) out_err: kfree(acomp); bus->audio_component = NULL; - dev_err(dev, "failed to add i915 component master (%d)\n", ret); + dev_info(dev, "failed to add i915 component master (%d)\n", ret); return ret; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c0bef11afa7e..86bbc6307c4f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2078,9 +2078,11 @@ static int azx_probe_continue(struct azx *chip) * for other chips, still continue probing as other * codecs can be on the same link. */ - if (CONTROLLER_IN_GPU(pci)) + if (CONTROLLER_IN_GPU(pci)) { + dev_err(chip->card->dev, + "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n"); goto out_free; - else + } else goto skip_i915; } From bca8e988043e39483afd7872a2641f03ed7201a6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 Jan 2016 15:46:44 +0100 Subject: [PATCH 08/11] ALSA: hda - Fix missing module loading with model=generic option When the generic codec driver is specified via model option or such, the hda driver doesn't try to load the generic driver module but still loads the codec-specific driver, and this ends up with the binding failure. This patch fixes it by moving the generic module request in the common helper code. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111021 Cc: # v4.4+ Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_bind.c | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 70671ad65d24..6efadbfb3fe3 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -174,14 +174,40 @@ static inline bool codec_probed(struct hda_codec *codec) return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; } +/* try to auto-load codec module */ +static void request_codec_module(struct hda_codec *codec) +{ +#ifdef MODULE + char modalias[32]; + const char *mod = NULL; + + switch (codec->probe_id) { + case HDA_CODEC_ID_GENERIC_HDMI: +#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) + mod = "snd-hda-codec-hdmi"; +#endif + break; + case HDA_CODEC_ID_GENERIC: +#if IS_MODULE(CONFIG_SND_HDA_GENERIC) + mod = "snd-hda-codec-generic"; +#endif + break; + default: + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + mod = modalias; + break; + } + + if (mod) + request_module(mod); +#endif /* MODULE */ +} + /* try to auto-load and bind the codec module */ static void codec_bind_module(struct hda_codec *codec) { #ifdef MODULE - char modalias[32]; - - snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); - request_module(modalias); + request_codec_module(codec); if (codec_probed(codec)) return; #endif @@ -218,17 +244,13 @@ static int codec_bind_generic(struct hda_codec *codec) if (is_likely_hdmi_codec(codec)) { codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; -#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) - request_module("snd-hda-codec-hdmi"); -#endif + request_codec_module(codec); if (codec_probed(codec)) return 0; } codec->probe_id = HDA_CODEC_ID_GENERIC; -#if IS_MODULE(CONFIG_SND_HDA_GENERIC) - request_module("snd-hda-codec-generic"); -#endif + request_codec_module(codec); if (codec_probed(codec)) return 0; return -ENODEV; From 991f86d7ae4e1f8c15806e62f97af519e3cdd860 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 Jan 2016 17:19:02 +0100 Subject: [PATCH 09/11] ALSA: hda - Flush the pending probe work at remove As HD-audio driver does deferred probe internally via workqueue, the driver might go into the mixed state doing both probe and remove when the module gets unloaded during the probe work. This eventually triggers an Oops, unsurprisingly. For avoiding this race, we just need to flush the pending probe work explicitly before actually starting the resource release. Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=960710 Cc: # v3.17+ Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 86bbc6307c4f..256e6cda218f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2151,9 +2151,17 @@ static int azx_probe_continue(struct azx *chip) static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip; + struct hda_intel *hda; + + if (card) { + /* flush the pending probing work */ + chip = card->private_data; + hda = container_of(chip, struct hda_intel, chip); + flush_work(&hda->probe_work); - if (card) snd_card_free(card); + } } static void azx_shutdown(struct pci_dev *pci) From 230323dac060123c340cf75997971145a42661ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 21 Jan 2016 17:19:31 +0100 Subject: [PATCH 10/11] ALSA: timer: Handle disconnection more safely Currently ALSA timer device doesn't take the disconnection into account very well; it merely unlinks the timer device at disconnection callback but does nothing else. Because of this, when an application accessing the timer device is disconnected, it may release the resource before actually closed. In most cases, it results in a warning message indicating a leftover timer instance like: ALSA: timer xxxx is busy? But basically this is an open race. This patch tries to address it. The strategy is like other ALSA devices: namely, - Manage card's refcount at each open/close - Wake up the pending tasks at disconnection - Check the shutdown flag appropriately at each possible call Note that this patch has one ugly hack to handle the wakeup of pending tasks. It'd be cleaner to introduce a new disconnect op to snd_timer_instance ops. But since it would lead to internal ABI breakage and it eventually increase my own work when backporting to stable kernels, I took a different path to implement locally in timer.c. A cleanup patch will follow at next for 4.5 kernel. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=109431 Cc: # v3.15+ Signed-off-by: Takashi Iwai --- sound/core/timer.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index cb25aded5349..681fb051b9eb 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -65,6 +65,7 @@ struct snd_timer_user { int qtail; int qused; int queue_size; + bool disconnected; struct snd_timer_read *queue; struct snd_timer_tread *tqueue; spinlock_t qlock; @@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti, mutex_unlock(®ister_mutex); return -ENOMEM; } + /* take a card refcount for safe disconnection */ + if (timer->card) + get_device(&timer->card->card_dev); timeri->slave_class = tid->dev_sclass; timeri->slave_id = slave_id; if (list_empty(&timer->open_list_head) && timer->hw.open) @@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri) } spin_unlock(&timer->lock); spin_unlock_irq(&slave_active_lock); + /* release a card refcount for safe disconnection */ + if (timer->card) + put_device(&timer->card->card_dev); mutex_unlock(®ister_mutex); } out: @@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) timer = timeri->timer; if (timer == NULL) return -EINVAL; + if (timer->card && timer->card->shutdown) + return -ENODEV; spin_lock_irqsave(&timer->lock, flags); timeri->ticks = timeri->cticks = ticks; timeri->pticks = 0; @@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) spin_lock_irqsave(&timer->lock, flags); list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); + if (timer->card && timer->card->shutdown) { + spin_unlock_irqrestore(&timer->lock, flags); + return 0; + } if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && !(--timer->running)) { timer->hw.stop(timer); @@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri) timer = timeri->timer; if (! timer) return -EINVAL; + if (timer->card && timer->card->shutdown) + return -ENODEV; spin_lock_irqsave(&timer->lock, flags); if (!timeri->cticks) timeri->cticks = 1; @@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg) unsigned long resolution, ticks; unsigned long flags; + if (timer->card && timer->card->shutdown) + return; + spin_lock_irqsave(&timer->lock, flags); /* now process all callbacks */ while (!list_empty(&timer->sack_list_head)) { @@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) if (timer == NULL) return; + if (timer->card && timer->card->shutdown) + return; + spin_lock_irqsave(&timer->lock, flags); /* remember the current resolution */ @@ -878,11 +899,28 @@ static int snd_timer_dev_register(struct snd_device *dev) return 0; } +/* just for reference in snd_timer_dev_disconnect() below */ +static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, + int event, struct timespec *tstamp, + unsigned long resolution); + static int snd_timer_dev_disconnect(struct snd_device *device) { struct snd_timer *timer = device->device_data; + struct snd_timer_instance *ti; + mutex_lock(®ister_mutex); list_del_init(&timer->device_list); + /* wake up pending sleepers */ + list_for_each_entry(ti, &timer->open_list_head, open_list) { + /* FIXME: better to have a ti.disconnect() op */ + if (ti->ccallback == snd_timer_user_ccallback) { + struct snd_timer_user *tu = ti->callback_data; + + tu->disconnected = true; + wake_up(&tu->qchange_sleep); + } + } mutex_unlock(®ister_mutex); return 0; } @@ -893,6 +931,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam unsigned long resolution = 0; struct snd_timer_instance *ti, *ts; + if (timer->card && timer->card->shutdown) + return; if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) return; if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || @@ -1051,6 +1091,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, mutex_lock(®ister_mutex); list_for_each_entry(timer, &snd_timer_list, device_list) { + if (timer->card && timer->card->shutdown) + continue; switch (timer->tmr_class) { case SNDRV_TIMER_CLASS_GLOBAL: snd_iprintf(buffer, "G%i: ", timer->tmr_device); @@ -1876,6 +1918,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, remove_wait_queue(&tu->qchange_sleep, &wait); + if (tu->disconnected) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; @@ -1925,6 +1971,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) mask = 0; if (tu->qused) mask |= POLLIN | POLLRDNORM; + if (tu->disconnected) + mask |= POLLERR; return mask; } From 40ed9444cd2421cceedb35bb8d8ff913a5ae1ac3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 21 Jan 2016 17:43:08 +0100 Subject: [PATCH 11/11] ALSA: timer: Introduce disconnect op to snd_timer_instance Instead of the previous ugly hack, introduce a new op, disconnect, to snd_timer_instance object for handling the wake up of pending tasks more cleanly. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=109431 Signed-off-by: Takashi Iwai --- include/sound/timer.h | 1 + sound/core/timer.c | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/sound/timer.h b/include/sound/timer.h index 7990469a44ce..c4d76ff056c6 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -104,6 +104,7 @@ struct snd_timer_instance { int event, struct timespec * tstamp, unsigned long resolution); + void (*disconnect)(struct snd_timer_instance *timeri); void *callback_data; unsigned long ticks; /* auto-load ticks when expired */ unsigned long cticks; /* current ticks */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 681fb051b9eb..af1f68f7e315 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -899,11 +899,6 @@ static int snd_timer_dev_register(struct snd_device *dev) return 0; } -/* just for reference in snd_timer_dev_disconnect() below */ -static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, - int event, struct timespec *tstamp, - unsigned long resolution); - static int snd_timer_dev_disconnect(struct snd_device *device) { struct snd_timer *timer = device->device_data; @@ -913,13 +908,8 @@ static int snd_timer_dev_disconnect(struct snd_device *device) list_del_init(&timer->device_list); /* wake up pending sleepers */ list_for_each_entry(ti, &timer->open_list_head, open_list) { - /* FIXME: better to have a ti.disconnect() op */ - if (ti->ccallback == snd_timer_user_ccallback) { - struct snd_timer_user *tu = ti->callback_data; - - tu->disconnected = true; - wake_up(&tu->qchange_sleep); - } + if (ti->disconnect) + ti->disconnect(ti); } mutex_unlock(®ister_mutex); return 0; @@ -1227,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, wake_up(&tu->qchange_sleep); } +static void snd_timer_user_disconnect(struct snd_timer_instance *timeri) +{ + struct snd_timer_user *tu = timeri->callback_data; + + tu->disconnected = true; + wake_up(&tu->qchange_sleep); +} + static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, unsigned long resolution, unsigned long ticks) @@ -1600,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file, ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; tu->timeri->ccallback = snd_timer_user_ccallback; tu->timeri->callback_data = (void *)tu; + tu->timeri->disconnect = snd_timer_user_disconnect; } __err: