]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Linux 4.5 compat: get_link() / put_link()
authorBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 14 Jan 2016 18:25:10 +0000 (13:25 -0500)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 20 Jan 2016 19:36:00 +0000 (11:36 -0800)
The follow_link() interface was retired in favor of get_link().
In the process of phasing in get_link() the Linux kernel went
through two different versions.  The first of which depended
on put_link() and the final version on a delayed done function.

- Improved configure checks for .follow_link, .get_link, .put_link.
  - Interfaces checked from newest to oldest.
  - Strict checking for each possible known interface.
  - Configure fails when no known interface is available.

- Both versions .get_link are detected and supported as well
  two previous versions of .follow_link.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tim Chase <tim@chase2k.com>
Signed-off-by: Chunwei Chen <tuxoko@gmail.com>
Issue #4228

config/kernel-follow-link-nameidata.m4 [deleted file]
config/kernel-get-link.m4 [new file with mode: 0644]
config/kernel-put-link-nameidata.m4 [deleted file]
config/kernel-put-link.m4 [new file with mode: 0644]
config/kernel.m4
module/zfs/zpl_inode.c

diff --git a/config/kernel-follow-link-nameidata.m4 b/config/kernel-follow-link-nameidata.m4
deleted file mode 100644 (file)
index 88c85ac..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-dnl #
-dnl # 4.2 API change
-dnl # This kernel retired the nameidata structure which forced the
-dnl # restructuring of the follow_link() prototype and how it is called.
-dnl # We check for the new interface rather than detecting the old one.
-dnl #
-AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_LINK], [
-       AC_MSG_CHECKING([whether iops->follow_link() passes nameidata])
-       ZFS_LINUX_TRY_COMPILE([
-               #include <linux/fs.h>
-               const char *follow_link(struct dentry *de, void **cookie)
-                   { return "symlink"; }
-               static struct inode_operations iops __attribute__ ((unused)) = {
-                       .follow_link = follow_link,
-               };
-       ],[
-       ],[
-               AC_MSG_RESULT(no)
-       ],[
-               AC_MSG_RESULT(yes)
-               AC_DEFINE(HAVE_FOLLOW_LINK_NAMEIDATA, 1,
-                         [iops->follow_link() nameidata])
-       ])
-])
diff --git a/config/kernel-get-link.m4 b/config/kernel-get-link.m4
new file mode 100644 (file)
index 0000000..022c49c
--- /dev/null
@@ -0,0 +1,100 @@
+dnl #
+dnl # Supported get_link() interfaces checked newest to oldest.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_LINK], [
+       dnl #
+       dnl # 4.2 API change
+       dnl # - This kernel retired the nameidata structure.
+       dnl #
+       AC_MSG_CHECKING([whether iops->follow_link() passes cookie])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+               const char *follow_link(struct dentry *de,
+                   void **cookie) { return "symlink"; }
+               static struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .follow_link = follow_link,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_FOLLOW_LINK_COOKIE, 1,
+                   [iops->follow_link() cookie])
+       ],[
+               dnl #
+               dnl # 2.6.32 API
+               dnl #
+               AC_MSG_RESULT(no)
+               AC_MSG_CHECKING(
+                  [whether iops->follow_link() passes nameidata])
+               ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+                       void *follow_link(struct dentry *de, struct
+                           nameidata *nd) { return (void *)NULL; }
+                       static struct inode_operations
+                           iops __attribute__ ((unused)) = {
+                               .follow_link = follow_link,
+                       };
+               ],[
+               ],[
+                       AC_MSG_RESULT(yes)
+                       AC_DEFINE(HAVE_FOLLOW_LINK_NAMEIDATA, 1,
+                                 [iops->follow_link() nameidata])
+               ],[
+                        AC_MSG_ERROR(no; please file a bug report)
+               ])
+       ])
+])
+
+AC_DEFUN([ZFS_AC_KERNEL_GET_LINK], [
+       dnl #
+       dnl # 4.5 API change
+       dnl # The get_link interface has added a delayed done call and
+       dnl # used it to retire the put_link() interface.
+       dnl #
+       AC_MSG_CHECKING([whether iops->get_link() passes delayed])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+               const char *get_link(struct dentry *de, struct inode *ip,
+                   struct delayed_call *done) { return "symlink"; }
+               static struct inode_operations
+                    iops __attribute__ ((unused)) = {
+                       .get_link = get_link,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_GET_LINK_DELAYED, 1,
+                   [iops->get_link() delayed])
+       ],[
+               dnl #
+               dnl # 4.5 API change
+               dnl # The follow_link() interface has been replaced by
+               dnl # get_link() which behaves the same as before except:
+               dnl # - An inode is passed as a separate argument
+               dnl # - When called in RCU mode a NULL dentry is passed.
+               dnl #
+               AC_MSG_RESULT(no)
+               AC_MSG_CHECKING([whether iops->get_link() passes cookie])
+               ZFS_LINUX_TRY_COMPILE([
+                       #include <linux/fs.h>
+                       const char *get_link(struct dentry *de, struct
+                           inode *ip, void **cookie) { return "symlink"; }
+                       static struct inode_operations
+                            iops __attribute__ ((unused)) = {
+                               .get_link = get_link,
+                       };
+               ],[
+               ],[
+                       AC_MSG_RESULT(yes)
+                       AC_DEFINE(HAVE_GET_LINK_COOKIE, 1,
+                           [iops->get_link() cookie])
+               ],[
+                       dnl #
+                       dnl # Check for the follow_link APIs.
+                       dnl #
+                       AC_MSG_RESULT(no)
+                       ZFS_AC_KERNEL_FOLLOW_LINK
+               ])
+       ])
+])
diff --git a/config/kernel-put-link-nameidata.m4 b/config/kernel-put-link-nameidata.m4
deleted file mode 100644 (file)
index 0181ae5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-dnl #
-dnl # 4.2 API change
-dnl # This kernel retired the nameidata structure which forced the
-dnl # restructuring of the put_link() prototype and how it is called.
-dnl # We check for the new interface rather than detecting the old one.
-dnl #
-AC_DEFUN([ZFS_AC_KERNEL_PUT_LINK], [
-       AC_MSG_CHECKING([whether iops->put_link() passes nameidata])
-       ZFS_LINUX_TRY_COMPILE([
-               #include <linux/fs.h>
-               void put_link(struct inode *ip, void *cookie) { return; }
-               static struct inode_operations iops __attribute__ ((unused)) = {
-                       .put_link = put_link,
-               };
-       ],[
-       ],[
-               AC_MSG_RESULT(no)
-       ],[
-               AC_MSG_RESULT(yes)
-               AC_DEFINE(HAVE_PUT_LINK_NAMEIDATA, 1,
-                         [iops->put_link() nameidata])
-       ])
-])
diff --git a/config/kernel-put-link.m4 b/config/kernel-put-link.m4
new file mode 100644 (file)
index 0000000..a0bb36e
--- /dev/null
@@ -0,0 +1,60 @@
+dnl #
+dnl # Supported symlink APIs
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_PUT_LINK], [
+       dnl #
+       dnl # 4.5 API change
+       dnl # get_link() uses delayed done, there is no put_link() interface.
+       dnl #
+       ZFS_LINUX_TRY_COMPILE([
+               #if !defined(HAVE_GET_LINK_DELAYED)
+               #error "Expecting get_link() delayed done"
+               #endif
+       ],[
+       ],[
+               AC_DEFINE(HAVE_PUT_LINK_DELAYED, 1, [iops->put_link() delayed])
+       ],[
+               dnl #
+               dnl # 4.2 API change
+               dnl # This kernel retired the nameidata structure.
+               dnl #
+               AC_MSG_CHECKING([whether iops->put_link() passes cookie])
+               ZFS_LINUX_TRY_COMPILE([
+                       #include <linux/fs.h>
+                       void put_link(struct inode *ip, void *cookie)
+                           { return; }
+                       static struct inode_operations
+                           iops __attribute__ ((unused)) = {
+                               .put_link = put_link,
+                       };
+               ],[
+               ],[
+                       AC_MSG_RESULT(yes)
+                       AC_DEFINE(HAVE_PUT_LINK_COOKIE, 1,
+                           [iops->put_link() cookie])
+               ],[
+                       dnl #
+                       dnl # 2.6.32 API
+                       dnl #
+                       AC_MSG_RESULT(no)
+                       AC_MSG_CHECKING(
+                           [whether iops->put_link() passes nameidata])
+                       ZFS_LINUX_TRY_COMPILE([
+                               #include <linux/fs.h>
+                               void put_link(struct dentry *de, struct
+                                   nameidata *nd, void *ptr) { return; }
+                               static struct inode_operations
+                                   iops __attribute__ ((unused)) = {
+                                       .put_link = put_link,
+                               };
+                       ],[
+                       ],[
+                               AC_MSG_RESULT(yes)
+                               AC_DEFINE(HAVE_PUT_LINK_NAMEIDATA, 1,
+                                   [iops->put_link() nameidata])
+                       ],[
+                               AC_MSG_ERROR(no; please file a bug report)
+                       ])
+               ])
+       ])
+])
index 16fb017fe4519a2146242ff21e3b3b15a02a9559..f63a056e4d0ff5fe7575c804f11b7ccae1af0b94 100644 (file)
@@ -58,7 +58,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
        ZFS_AC_KERNEL_MKDIR_UMODE_T
        ZFS_AC_KERNEL_LOOKUP_NAMEIDATA
        ZFS_AC_KERNEL_CREATE_NAMEIDATA
-       ZFS_AC_KERNEL_FOLLOW_LINK
+       ZFS_AC_KERNEL_GET_LINK
        ZFS_AC_KERNEL_PUT_LINK
        ZFS_AC_KERNEL_TRUNCATE_RANGE
        ZFS_AC_KERNEL_AUTOMOUNT
index 69a5db4daab5983fc10c593c3dfc4ca5c2faddab..c4233959b0e6865a2b2aa2383d2cef47696e5c59 100644 (file)
@@ -367,26 +367,42 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
        return (error);
 }
 
-#ifdef HAVE_FOLLOW_LINK_NAMEIDATA
-static void *
-zpl_follow_link(struct dentry *dentry, struct nameidata *nd)
-#else
-const char *
-zpl_follow_link(struct dentry *dentry, void **symlink_cookie)
+#if defined(HAVE_PUT_LINK_COOKIE)
+static void
+zpl_put_link(struct inode *unused, void *cookie)
+{
+       kmem_free(cookie, MAXPATHLEN);
+}
+#elif defined(HAVE_PUT_LINK_NAMEIDATA)
+static void
+zpl_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+       const char *link = nd_get_link(nd);
+
+       if (!IS_ERR(link))
+               kmem_free(link, MAXPATHLEN);
+}
+#elif defined(HAVE_PUT_LINK_DELAYED)
+static void
+zpl_put_link(void *ptr)
+{
+       kmem_free(ptr, MAXPATHLEN);
+}
 #endif
+
+static int
+zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link)
 {
+       fstrans_cookie_t cookie;
        cred_t *cr = CRED();
-       struct inode *ip = dentry->d_inode;
        struct iovec iov;
        uio_t uio;
-       char *link;
        int error;
-       fstrans_cookie_t cookie;
 
        crhold(cr);
-
+       *link = NULL;
        iov.iov_len = MAXPATHLEN;
-       iov.iov_base = link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+       iov.iov_base = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
 
        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;
@@ -397,41 +413,78 @@ zpl_follow_link(struct dentry *dentry, void **symlink_cookie)
        cookie = spl_fstrans_mark();
        error = -zfs_readlink(ip, &uio, cr);
        spl_fstrans_unmark(cookie);
-
-       if (error)
-               kmem_free(link, MAXPATHLEN);
-
        crfree(cr);
 
-#ifdef HAVE_FOLLOW_LINK_NAMEIDATA
        if (error)
-               nd_set_link(nd, ERR_PTR(error));
+               kmem_free(iov.iov_base, MAXPATHLEN);
        else
-               nd_set_link(nd, link);
+               *link = iov.iov_base;
 
-       return (NULL);
-#else
+       return (error);
+}
+
+#if defined(HAVE_GET_LINK_DELAYED)
+const char *
+zpl_get_link(struct dentry *dentry, struct inode *inode,
+    struct delayed_call *done)
+{
+       char *link = NULL;
+       int error;
+
+       if (!dentry)
+               return (ERR_PTR(-ECHILD));
+
+       error = zpl_get_link_common(dentry, inode, &link);
        if (error)
                return (ERR_PTR(error));
-       else
-               return (*symlink_cookie = link);
-#endif
+
+       set_delayed_call(done, zpl_put_link, link);
+
+       return (link);
 }
+#elif defined(HAVE_GET_LINK_COOKIE)
+const char *
+zpl_get_link(struct dentry *dentry, struct inode *inode, void **cookie)
+{
+       char *link = NULL;
+       int error;
 
-#ifdef HAVE_PUT_LINK_NAMEIDATA
-static void
-zpl_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+       if (!dentry)
+               return (ERR_PTR(-ECHILD));
+
+       error = zpl_get_link_common(dentry, inode, &link);
+       if (error)
+               return (ERR_PTR(error));
+
+       return (*cookie = link);
+}
+#elif defined(HAVE_FOLLOW_LINK_COOKIE)
+const char *
+zpl_follow_link(struct dentry *dentry, void **cookie)
 {
-       const char *link = nd_get_link(nd);
+       char *link = NULL;
+       int error;
 
-       if (!IS_ERR(link))
-               kmem_free(link, MAXPATHLEN);
+       error = zpl_get_link_common(dentry, dentry->d_inode, &link);
+       if (error)
+               return (ERR_PTR(error));
+
+       return (*cookie = link);
 }
-#else
-static void
-zpl_put_link(struct inode *unused, void *symlink_cookie)
+#elif defined(HAVE_FOLLOW_LINK_NAMEIDATA)
+static void *
+zpl_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-       kmem_free(symlink_cookie, MAXPATHLEN);
+       char *link = NULL;
+       int error;
+
+       error = zpl_get_link_common(dentry, dentry->d_inode, &link);
+       if (error)
+               nd_set_link(nd, ERR_PTR(error));
+       else
+               nd_set_link(nd, link);
+
+       return (NULL);
 }
 #endif
 
@@ -619,8 +672,14 @@ const struct inode_operations zpl_dir_inode_operations = {
 
 const struct inode_operations zpl_symlink_inode_operations = {
        .readlink       = generic_readlink,
+#if defined(HAVE_GET_LINK_DELAYED) || defined(HAVE_GET_LINK_COOKIE)
+       .get_link       = zpl_get_link,
+#elif defined(HAVE_FOLLOW_LINK_COOKIE) || defined(HAVE_FOLLOW_LINK_NAMEIDATA)
        .follow_link    = zpl_follow_link,
+#endif
+#if defined(HAVE_PUT_LINK_COOKIE) || defined(HAVE_PUT_LINK_NAMEIDATA)
        .put_link       = zpl_put_link,
+#endif
        .setattr        = zpl_setattr,
        .getattr        = zpl_getattr,
        .setxattr       = generic_setxattr,