diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-10-17 12:03:24 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-10-17 13:23:40 -0400 |
commit | cbbaa603a03cc46681e24d6b2804b62fde95a2af (patch) | |
tree | e6e2be9cd9a7c53c1dc527ab8c7c4e99910ea8e4 /sound/pci/hda/patch_hdmi.c | |
parent | 4758fed912d7cd0ba53d2694e89b884114de6580 (diff) |
ALSA: hda - Fix possible races in HDMI driver
Some per_pin fields and ELD contents might be changed dynamically in
multiple ways where the concurrent accesses are still opened in the
current code. This patch fixes such possible races by using eld->lock
in appropriate places.
Reported-by: Anssi Hannula <anssi.hannula@iki.fi>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b899eba6f279..c2bad9587d54 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -1342,6 +1342,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) | |||
1342 | bool update_eld = false; | 1342 | bool update_eld = false; |
1343 | bool eld_changed = false; | 1343 | bool eld_changed = false; |
1344 | 1344 | ||
1345 | mutex_lock(&pin_eld->lock); | ||
1345 | pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); | 1346 | pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); |
1346 | if (pin_eld->monitor_present) | 1347 | if (pin_eld->monitor_present) |
1347 | eld->eld_valid = !!(present & AC_PINSENSE_ELDV); | 1348 | eld->eld_valid = !!(present & AC_PINSENSE_ELDV); |
@@ -1371,11 +1372,10 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) | |||
1371 | queue_delayed_work(codec->bus->workq, | 1372 | queue_delayed_work(codec->bus->workq, |
1372 | &per_pin->work, | 1373 | &per_pin->work, |
1373 | msecs_to_jiffies(300)); | 1374 | msecs_to_jiffies(300)); |
1374 | return; | 1375 | goto unlock; |
1375 | } | 1376 | } |
1376 | } | 1377 | } |
1377 | 1378 | ||
1378 | mutex_lock(&pin_eld->lock); | ||
1379 | if (pin_eld->eld_valid && !eld->eld_valid) { | 1379 | if (pin_eld->eld_valid && !eld->eld_valid) { |
1380 | update_eld = true; | 1380 | update_eld = true; |
1381 | eld_changed = true; | 1381 | eld_changed = true; |
@@ -1400,12 +1400,13 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) | |||
1400 | hdmi_setup_audio_infoframe(codec, per_pin, | 1400 | hdmi_setup_audio_infoframe(codec, per_pin, |
1401 | per_pin->non_pcm); | 1401 | per_pin->non_pcm); |
1402 | } | 1402 | } |
1403 | mutex_unlock(&pin_eld->lock); | ||
1404 | 1403 | ||
1405 | if (eld_changed) | 1404 | if (eld_changed) |
1406 | snd_ctl_notify(codec->bus->card, | 1405 | snd_ctl_notify(codec->bus->card, |
1407 | SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, | 1406 | SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, |
1408 | &per_pin->eld_ctl->id); | 1407 | &per_pin->eld_ctl->id); |
1408 | unlock: | ||
1409 | mutex_unlock(&pin_eld->lock); | ||
1409 | } | 1410 | } |
1410 | 1411 | ||
1411 | static void hdmi_repoll_eld(struct work_struct *work) | 1412 | static void hdmi_repoll_eld(struct work_struct *work) |
@@ -1576,10 +1577,12 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, | |||
1576 | bool non_pcm; | 1577 | bool non_pcm; |
1577 | 1578 | ||
1578 | non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); | 1579 | non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); |
1580 | mutex_lock(&per_pin->sink_eld.lock); | ||
1579 | per_pin->channels = substream->runtime->channels; | 1581 | per_pin->channels = substream->runtime->channels; |
1580 | per_pin->setup = true; | 1582 | per_pin->setup = true; |
1581 | 1583 | ||
1582 | hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); | 1584 | hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); |
1585 | mutex_unlock(&per_pin->sink_eld.lock); | ||
1583 | 1586 | ||
1584 | return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); | 1587 | return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); |
1585 | } | 1588 | } |
@@ -1617,11 +1620,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, | |||
1617 | per_pin = get_pin(spec, pin_idx); | 1620 | per_pin = get_pin(spec, pin_idx); |
1618 | 1621 | ||
1619 | snd_hda_spdif_ctls_unassign(codec, pin_idx); | 1622 | snd_hda_spdif_ctls_unassign(codec, pin_idx); |
1623 | |||
1624 | mutex_lock(&per_pin->sink_eld.lock); | ||
1620 | per_pin->chmap_set = false; | 1625 | per_pin->chmap_set = false; |
1621 | memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); | 1626 | memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); |
1622 | 1627 | ||
1623 | per_pin->setup = false; | 1628 | per_pin->setup = false; |
1624 | per_pin->channels = 0; | 1629 | per_pin->channels = 0; |
1630 | mutex_unlock(&per_pin->sink_eld.lock); | ||
1625 | } | 1631 | } |
1626 | 1632 | ||
1627 | return 0; | 1633 | return 0; |
@@ -1750,10 +1756,12 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, | |||
1750 | ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); | 1756 | ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); |
1751 | if (ca < 0) | 1757 | if (ca < 0) |
1752 | return -EINVAL; | 1758 | return -EINVAL; |
1759 | mutex_lock(&per_pin->sink_eld.lock); | ||
1753 | per_pin->chmap_set = true; | 1760 | per_pin->chmap_set = true; |
1754 | memcpy(per_pin->chmap, chmap, sizeof(chmap)); | 1761 | memcpy(per_pin->chmap, chmap, sizeof(chmap)); |
1755 | if (prepared) | 1762 | if (prepared) |
1756 | hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); | 1763 | hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); |
1764 | mutex_unlock(&per_pin->sink_eld.lock); | ||
1757 | 1765 | ||
1758 | return 0; | 1766 | return 0; |
1759 | } | 1767 | } |