summary refs log tree commit diff
path: root/sound/hda/hdac_device.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-10-18 15:51:59 +0200
committerTakashi Iwai <tiwai@suse.de>2017-10-19 13:58:36 +0200
commit9780ded39bef5d22a84bdc39112df93f70a58bdd (patch)
tree312c860555df48fda2450d833a2cb1a7a7cd5b0e /sound/hda/hdac_device.c
parent28d1d6d2f314ff395ff67565d1145742614b21c8 (diff)
downloadlinux-9780ded39bef5d22a84bdc39112df93f70a58bdd.tar.gz
ALSA: hda: Avoid racy recreation of widget kobjects
The refresh of HD-audio widget sysfs kobjects via
snd_hdac_refresh_widget_sysfs() is slightly racy.
The driver recreates the whole tree from scratch after deleting the
whole.  When CONFIG_DEBUG_KOBJECT_RELEASE option is used, kobject
release doesn't happen immediately but delayed, while the re-creation
of the same named kobject happens soon after invoking kobject_put().
This may end up with the conflicts of duplicated kobjects, as found in
the bug report below.

In this patch, we take another approach to refresh the tree: instead
of recreating the whole tree, just add the new nodes and delete the
non-existing nodes.  Since the refresh happens only once at
initialization, no longer race would happen.

Along with the code change, merge snd_hdac_refresh_widget_sysfs() with
the existing snd_hdac_refresh_widgets() with an additional bool flag
for simplifying the code.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=197307
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/hda/hdac_device.c')
-rw-r--r--sound/hda/hdac_device.c43
1 files changed, 10 insertions, 33 deletions
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index 19deb306facb..06f845e293cb 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -87,7 +87,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
 
 	fg = codec->afg ? codec->afg : codec->mfg;
 
-	err = snd_hdac_refresh_widgets(codec);
+	err = snd_hdac_refresh_widgets(codec, false);
 	if (err < 0)
 		goto error;
 
@@ -388,11 +388,12 @@ static void setup_fg_nodes(struct hdac_device *codec)
 /**
  * snd_hdac_refresh_widgets - Reset the widget start/end nodes
  * @codec: the codec object
+ * @sysfs: re-initialize sysfs tree, too
  */
-int snd_hdac_refresh_widgets(struct hdac_device *codec)
+int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
 {
 	hda_nid_t start_nid;
-	int nums;
+	int nums, err;
 
 	nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
 	if (!start_nid || nums <= 0 || nums >= 0xff) {
@@ -401,6 +402,12 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)
 		return -EINVAL;
 	}
 
+	if (sysfs) {
+		err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+		if (err < 0)
+			return err;
+	}
+
 	codec->num_nodes = nums;
 	codec->start_nid = start_nid;
 	codec->end_nid = start_nid + nums;
@@ -408,36 +415,6 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
 
-/**
- * snd_hdac_refresh_widget_sysfs - Reset the codec widgets and reinit the
- * codec sysfs
- * @codec: the codec object
- *
- * first we need to remove sysfs, then refresh widgets and lastly
- * recreate it
- */
-int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec)
-{
-	int ret;
-
-	if (device_is_registered(&codec->dev))
-		hda_widget_sysfs_exit(codec);
-	ret = snd_hdac_refresh_widgets(codec);
-	if (ret) {
-		dev_err(&codec->dev, "failed to refresh widget: %d\n", ret);
-		return ret;
-	}
-	if (device_is_registered(&codec->dev)) {
-		ret = hda_widget_sysfs_init(codec);
-		if (ret) {
-			dev_err(&codec->dev, "failed to init sysfs: %d\n", ret);
-			return ret;
-		}
-	}
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_refresh_widget_sysfs);
-
 /* return CONNLIST_LEN parameter of the given widget */
 static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
 {