diff mbox

[tpmdd-devel] tpm: enable PPI for TPM 2.0

Message ID 1429199681-16596-1-git-send-email-jarkko.sakkinen@linux.intel.com
State Superseded
Headers show

Commit Message

Jarkko Sakkinen April 16, 2015, 3:54 p.m. UTC
Added PPI interface to the character device sysfs directory accessible
both for 1.x and 2.0 devices.

In order to maintain backwards compatibility when using 1.x devices,
symlink is created from the pdev to the PPI directory. For this reason
we need to export kernfs_create_link() and kernfs_remove_by_name_ns().

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Jason Gunthorpe <jason.gunthorpe@obsidianresearch.com>
---
 drivers/char/tpm/tpm-chip.c | 28 ++++++++++++++++++++--------
 drivers/char/tpm/tpm.h      | 14 +++++---------
 drivers/char/tpm/tpm_ppi.c  | 34 +++++++++++-----------------------
 fs/kernfs/dir.c             |  1 +
 fs/kernfs/symlink.c         |  1 +
 5 files changed, 38 insertions(+), 40 deletions(-)

Comments

Jarkko Sakkinen April 17, 2015, 12:42 p.m. UTC | #1
On Thu, Apr 16, 2015 at 06:54:39PM +0300, Jarkko Sakkinen wrote:
> Added PPI interface to the character device sysfs directory accessible
> both for 1.x and 2.0 devices.
> 
> In order to maintain backwards compatibility when using 1.x devices,
> symlink is created from the pdev to the PPI directory. For this reason
> we need to export kernfs_create_link() and kernfs_remove_by_name_ns().
> 
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Reviewed-by: Jason Gunthorpe <jason.gunthorpe@obsidianresearch.com>
> ---
>  drivers/char/tpm/tpm-chip.c | 28 ++++++++++++++++++++--------
>  drivers/char/tpm/tpm.h      | 14 +++++---------
>  drivers/char/tpm/tpm_ppi.c  | 34 +++++++++++-----------------------
>  fs/kernfs/dir.c             |  1 +
>  fs/kernfs/symlink.c         |  1 +
>  5 files changed, 38 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
> index 283f00a..34930ef 100644
> --- a/drivers/char/tpm/tpm-chip.c
> +++ b/drivers/char/tpm/tpm-chip.c
> @@ -26,6 +26,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/freezer.h>
>  #include <linux/major.h>
> +#include <linux/kernfs.h>
>  #include "tpm.h"
>  #include "tpm_eventlog.h"
>  
> @@ -119,6 +120,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
>  	chip->dev.class = tpm_class;
>  	chip->dev.release = tpm_dev_release;
>  	chip->dev.parent = chip->pdev;
> +#ifdef CONFIG_ACPI
> +	chip->dev.groups = chip->groups;
> +#endif
>  
>  	if (chip->dev_num == 0)
>  		chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
> @@ -181,12 +185,6 @@ static int tpm1_chip_register(struct tpm_chip *chip)
>  	if (rc)
>  		return rc;
>  
> -	rc = tpm_add_ppi(chip);
> -	if (rc) {
> -		tpm_sysfs_del_device(chip);
> -		return rc;
> -	}
> -
>  	chip->bios_dir = tpm_bios_log_setup(chip->devname);
>  
>  	return 0;
> @@ -200,8 +198,6 @@ static void tpm1_chip_unregister(struct tpm_chip *chip)
>  	if (chip->bios_dir)
>  		tpm_bios_log_teardown(chip->bios_dir);
>  
> -	tpm_remove_ppi(chip);
> -
>  	tpm_sysfs_del_device(chip);
>  }
>  
> @@ -218,16 +214,29 @@ static void tpm1_chip_unregister(struct tpm_chip *chip)
>   */
>  int tpm_chip_register(struct tpm_chip *chip)
>  {
> +	struct kernfs_node *target;
> +	struct kernfs_node *link;
>  	int rc;
>  
>  	rc = tpm1_chip_register(chip);
>  	if (rc)
>  		return rc;
>  
> +	tpm_add_ppi(chip);
> +
>  	rc = tpm_dev_add_device(chip);
>  	if (rc)
>  		goto out_err;
>  
> +	if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
> +		target = kernfs_find_and_get(chip->dev.kobj.sd, "ppi");

Check that valid node was returned.

> +		link = kernfs_create_link(chip->pdev->kobj.sd, "ppi", target);
> +		if (IS_ERR(link)) {
> +			rc = PTR_ERR(link);
> +			goto out_err;
> +		}

Oops, kernfs_put() missing from the patch.


> +	}
> +
>  	/* Make the chip available. */
>  	spin_lock(&driver_lock);
>  	list_add_rcu(&chip->list, &tpm_chip_list);
> @@ -262,6 +271,9 @@ void tpm_chip_unregister(struct tpm_chip *chip)
>  	spin_unlock(&driver_lock);
>  	synchronize_rcu();
>  
> +	if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
> +		kernfs_remove_by_name(chip->pdev->kobj.sd, "ppi");
> +
>  	tpm1_chip_unregister(chip);
>  	tpm_dev_del_device(chip);
>  }
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index f8319a0..bbb1fae 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -175,6 +175,8 @@ struct tpm_chip {
>  	struct dentry **bios_dir;
>  
>  #ifdef CONFIG_ACPI
> +	const struct attribute_group *groups[2];
> +	unsigned int groups_cnt;
>  	acpi_handle acpi_dev_handle;
>  	char ppi_version[TPM_PPI_VERSION_LEN + 1];
>  #endif /* CONFIG_ACPI */
> @@ -182,7 +184,7 @@ struct tpm_chip {
>  	struct list_head list;
>  };
>  
> -#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
> +#define to_tpm_chip(n) container_of(dev, struct tpm_chip, dev)
>  
>  static inline void tpm_chip_put(struct tpm_chip *chip)
>  {
> @@ -412,15 +414,9 @@ void tpm_sysfs_del_device(struct tpm_chip *chip);
>  int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
>  
>  #ifdef CONFIG_ACPI
> -extern int tpm_add_ppi(struct tpm_chip *chip);
> -extern void tpm_remove_ppi(struct tpm_chip *chip);
> +extern void tpm_add_ppi(struct tpm_chip *chip);
>  #else
> -static inline int tpm_add_ppi(struct tpm_chip *chip)
> -{
> -	return 0;
> -}
> -
> -static inline void tpm_remove_ppi(struct tpm_chip *chip)
> +static inline void tpm_add_ppi(struct tpm_chip *chip)
>  {
>  }
>  #endif
> diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
> index 6ca9b5d..692a2c6 100644
> --- a/drivers/char/tpm/tpm_ppi.c
> +++ b/drivers/char/tpm/tpm_ppi.c
> @@ -53,7 +53,7 @@ tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type,
>  static ssize_t tpm_show_ppi_version(struct device *dev,
>  				    struct device_attribute *attr, char *buf)
>  {
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version);
>  }
> @@ -63,7 +63,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev,
>  {
>  	ssize_t size = -EINVAL;
>  	union acpi_object *obj;
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ,
>  			   ACPI_TYPE_PACKAGE, NULL);
> @@ -100,7 +100,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev,
>  	int func = TPM_PPI_FN_SUBREQ;
>  	union acpi_object *obj, tmp;
>  	union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	/*
>  	 * the function to submit TPM operation request to pre-os environment
> @@ -156,7 +156,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev,
>  		.buffer.length = 0,
>  		.buffer.pointer = NULL
>  	};
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	static char *info[] = {
>  		"None",
> @@ -197,7 +197,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev,
>  	acpi_status status = -EINVAL;
>  	union acpi_object *obj, *ret_obj;
>  	u64 req, res;
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP,
>  			   ACPI_TYPE_PACKAGE, NULL);
> @@ -296,7 +296,7 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
>  					   struct device_attribute *attr,
>  					   char *buf)
>  {
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	return show_ppi_operations(chip->acpi_dev_handle, buf, 0,
>  				   PPI_TPM_REQ_MAX);
> @@ -306,7 +306,7 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
>  					  struct device_attribute *attr,
>  					  char *buf)
>  {
> -	struct tpm_chip *chip = dev_get_drvdata(dev);
> +	struct tpm_chip *chip = to_tpm_chip(dev);
>  
>  	return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START,
>  				   PPI_VS_REQ_END);
> @@ -334,17 +334,16 @@ static struct attribute_group ppi_attr_grp = {
>  	.attrs = ppi_attrs
>  };
>  
> -int tpm_add_ppi(struct tpm_chip *chip)
> +void tpm_add_ppi(struct tpm_chip *chip)
>  {
>  	union acpi_object *obj;
> -	int rc;
>  
>  	if (!chip->acpi_dev_handle)
> -		return 0;
> +		return;
>  
>  	if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid,
>  			    TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION))
> -		return 0;
> +		return;
>  
>  	/* Cache PPI version string. */
>  	obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid,
> @@ -356,16 +355,5 @@ int tpm_add_ppi(struct tpm_chip *chip)
>  		ACPI_FREE(obj);
>  	}
>  
> -	rc = sysfs_create_group(&chip->pdev->kobj, &ppi_attr_grp);
> -
> -	if (!rc)
> -		chip->flags |= TPM_CHIP_FLAG_PPI;
> -
> -	return rc;
> -}
> -
> -void tpm_remove_ppi(struct tpm_chip *chip)
> -{
> -	if (chip->flags & TPM_CHIP_FLAG_PPI)
> -		sysfs_remove_group(&chip->pdev->kobj, &ppi_attr_grp);
> +	chip->groups[chip->groups_cnt++] = &ppi_attr_grp;
>  }
> diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
> index 6acc964..749cea3 100644
> --- a/fs/kernfs/dir.c
> +++ b/fs/kernfs/dir.c
> @@ -1225,6 +1225,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
>  	else
>  		return -ENOENT;
>  }
> +EXPORT_SYMBOL_GPL(kernfs_remove_by_name_ns);
>  
>  /**
>   * kernfs_rename_ns - move and rename a kernfs_node
> diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
> index 8a19889..fd70d1d 100644
> --- a/fs/kernfs/symlink.c
> +++ b/fs/kernfs/symlink.c
> @@ -45,6 +45,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
>  	kernfs_put(kn);
>  	return ERR_PTR(error);
>  }
> +EXPORT_SYMBOL_GPL(kernfs_create_link);
>  
>  static int kernfs_get_target_path(struct kernfs_node *parent,
>  				  struct kernfs_node *target, char *path)
> -- 
> 2.1.4
> 

Other: remove TPM_CHIP_FLAG_PPI from tpm.h.

/Jarkko

------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
diff mbox

Patch

diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 283f00a..34930ef 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -26,6 +26,7 @@ 
 #include <linux/spinlock.h>
 #include <linux/freezer.h>
 #include <linux/major.h>
+#include <linux/kernfs.h>
 #include "tpm.h"
 #include "tpm_eventlog.h"
 
@@ -119,6 +120,9 @@  struct tpm_chip *tpmm_chip_alloc(struct device *dev,
 	chip->dev.class = tpm_class;
 	chip->dev.release = tpm_dev_release;
 	chip->dev.parent = chip->pdev;
+#ifdef CONFIG_ACPI
+	chip->dev.groups = chip->groups;
+#endif
 
 	if (chip->dev_num == 0)
 		chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
@@ -181,12 +185,6 @@  static int tpm1_chip_register(struct tpm_chip *chip)
 	if (rc)
 		return rc;
 
-	rc = tpm_add_ppi(chip);
-	if (rc) {
-		tpm_sysfs_del_device(chip);
-		return rc;
-	}
-
 	chip->bios_dir = tpm_bios_log_setup(chip->devname);
 
 	return 0;
@@ -200,8 +198,6 @@  static void tpm1_chip_unregister(struct tpm_chip *chip)
 	if (chip->bios_dir)
 		tpm_bios_log_teardown(chip->bios_dir);
 
-	tpm_remove_ppi(chip);
-
 	tpm_sysfs_del_device(chip);
 }
 
@@ -218,16 +214,29 @@  static void tpm1_chip_unregister(struct tpm_chip *chip)
  */
 int tpm_chip_register(struct tpm_chip *chip)
 {
+	struct kernfs_node *target;
+	struct kernfs_node *link;
 	int rc;
 
 	rc = tpm1_chip_register(chip);
 	if (rc)
 		return rc;
 
+	tpm_add_ppi(chip);
+
 	rc = tpm_dev_add_device(chip);
 	if (rc)
 		goto out_err;
 
+	if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
+		target = kernfs_find_and_get(chip->dev.kobj.sd, "ppi");
+		link = kernfs_create_link(chip->pdev->kobj.sd, "ppi", target);
+		if (IS_ERR(link)) {
+			rc = PTR_ERR(link);
+			goto out_err;
+		}
+	}
+
 	/* Make the chip available. */
 	spin_lock(&driver_lock);
 	list_add_rcu(&chip->list, &tpm_chip_list);
@@ -262,6 +271,9 @@  void tpm_chip_unregister(struct tpm_chip *chip)
 	spin_unlock(&driver_lock);
 	synchronize_rcu();
 
+	if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+		kernfs_remove_by_name(chip->pdev->kobj.sd, "ppi");
+
 	tpm1_chip_unregister(chip);
 	tpm_dev_del_device(chip);
 }
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index f8319a0..bbb1fae 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -175,6 +175,8 @@  struct tpm_chip {
 	struct dentry **bios_dir;
 
 #ifdef CONFIG_ACPI
+	const struct attribute_group *groups[2];
+	unsigned int groups_cnt;
 	acpi_handle acpi_dev_handle;
 	char ppi_version[TPM_PPI_VERSION_LEN + 1];
 #endif /* CONFIG_ACPI */
@@ -182,7 +184,7 @@  struct tpm_chip {
 	struct list_head list;
 };
 
-#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
+#define to_tpm_chip(n) container_of(dev, struct tpm_chip, dev)
 
 static inline void tpm_chip_put(struct tpm_chip *chip)
 {
@@ -412,15 +414,9 @@  void tpm_sysfs_del_device(struct tpm_chip *chip);
 int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
 
 #ifdef CONFIG_ACPI
-extern int tpm_add_ppi(struct tpm_chip *chip);
-extern void tpm_remove_ppi(struct tpm_chip *chip);
+extern void tpm_add_ppi(struct tpm_chip *chip);
 #else
-static inline int tpm_add_ppi(struct tpm_chip *chip)
-{
-	return 0;
-}
-
-static inline void tpm_remove_ppi(struct tpm_chip *chip)
+static inline void tpm_add_ppi(struct tpm_chip *chip)
 {
 }
 #endif
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
index 6ca9b5d..692a2c6 100644
--- a/drivers/char/tpm/tpm_ppi.c
+++ b/drivers/char/tpm/tpm_ppi.c
@@ -53,7 +53,7 @@  tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type,
 static ssize_t tpm_show_ppi_version(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version);
 }
@@ -63,7 +63,7 @@  static ssize_t tpm_show_ppi_request(struct device *dev,
 {
 	ssize_t size = -EINVAL;
 	union acpi_object *obj;
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ,
 			   ACPI_TYPE_PACKAGE, NULL);
@@ -100,7 +100,7 @@  static ssize_t tpm_store_ppi_request(struct device *dev,
 	int func = TPM_PPI_FN_SUBREQ;
 	union acpi_object *obj, tmp;
 	union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	/*
 	 * the function to submit TPM operation request to pre-os environment
@@ -156,7 +156,7 @@  static ssize_t tpm_show_ppi_transition_action(struct device *dev,
 		.buffer.length = 0,
 		.buffer.pointer = NULL
 	};
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	static char *info[] = {
 		"None",
@@ -197,7 +197,7 @@  static ssize_t tpm_show_ppi_response(struct device *dev,
 	acpi_status status = -EINVAL;
 	union acpi_object *obj, *ret_obj;
 	u64 req, res;
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP,
 			   ACPI_TYPE_PACKAGE, NULL);
@@ -296,7 +296,7 @@  static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
 					   struct device_attribute *attr,
 					   char *buf)
 {
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	return show_ppi_operations(chip->acpi_dev_handle, buf, 0,
 				   PPI_TPM_REQ_MAX);
@@ -306,7 +306,7 @@  static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
 					  struct device_attribute *attr,
 					  char *buf)
 {
-	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_chip *chip = to_tpm_chip(dev);
 
 	return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START,
 				   PPI_VS_REQ_END);
@@ -334,17 +334,16 @@  static struct attribute_group ppi_attr_grp = {
 	.attrs = ppi_attrs
 };
 
-int tpm_add_ppi(struct tpm_chip *chip)
+void tpm_add_ppi(struct tpm_chip *chip)
 {
 	union acpi_object *obj;
-	int rc;
 
 	if (!chip->acpi_dev_handle)
-		return 0;
+		return;
 
 	if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid,
 			    TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION))
-		return 0;
+		return;
 
 	/* Cache PPI version string. */
 	obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid,
@@ -356,16 +355,5 @@  int tpm_add_ppi(struct tpm_chip *chip)
 		ACPI_FREE(obj);
 	}
 
-	rc = sysfs_create_group(&chip->pdev->kobj, &ppi_attr_grp);
-
-	if (!rc)
-		chip->flags |= TPM_CHIP_FLAG_PPI;
-
-	return rc;
-}
-
-void tpm_remove_ppi(struct tpm_chip *chip)
-{
-	if (chip->flags & TPM_CHIP_FLAG_PPI)
-		sysfs_remove_group(&chip->pdev->kobj, &ppi_attr_grp);
+	chip->groups[chip->groups_cnt++] = &ppi_attr_grp;
 }
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 6acc964..749cea3 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1225,6 +1225,7 @@  int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
 	else
 		return -ENOENT;
 }
+EXPORT_SYMBOL_GPL(kernfs_remove_by_name_ns);
 
 /**
  * kernfs_rename_ns - move and rename a kernfs_node
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 8a19889..fd70d1d 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -45,6 +45,7 @@  struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
 	kernfs_put(kn);
 	return ERR_PTR(error);
 }
+EXPORT_SYMBOL_GPL(kernfs_create_link);
 
 static int kernfs_get_target_path(struct kernfs_node *parent,
 				  struct kernfs_node *target, char *path)