memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
 
-static unsigned int
-nfs4_init_nonuniform_client_string(struct nfs_client *clp,
-                                  char *buf, size_t len)
+static int
+nfs4_init_nonuniform_client_string(struct nfs_client *clp)
 {
-       unsigned int result;
+       int result;
+       size_t len;
+       char *str;
+       bool retried = false;
 
        if (clp->cl_owner_id != NULL)
-               return strlcpy(buf, clp->cl_owner_id, len);
+               return 0;
+retry:
+       rcu_read_lock();
+       len = 10 + strlen(clp->cl_ipaddr) + 1 +
+               strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) +
+               1 +
+               strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO)) +
+               1;
+       rcu_read_unlock();
+
+       if (len > NFS4_OPAQUE_LIMIT + 1)
+               return -EINVAL;
+
+       /*
+        * Since this string is allocated at mount time, and held until the
+        * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying
+        * about a memory-reclaim deadlock.
+        */
+       str = kmalloc(len, GFP_KERNEL);
+       if (!str)
+               return -ENOMEM;
 
        rcu_read_lock();
-       result = scnprintf(buf, len, "Linux NFSv4.0 %s/%s %s",
-                               clp->cl_ipaddr,
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_ADDR),
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_PROTO));
+       result = scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
+                       clp->cl_ipaddr,
+                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
+                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO));
        rcu_read_unlock();
-       clp->cl_owner_id = kstrdup(buf, GFP_KERNEL);
-       return result;
+
+       /* Did something change? */
+       if (result >= len) {
+               kfree(str);
+               if (retried)
+                       return -EINVAL;
+               retried = true;
+               goto retry;
+       }
+       clp->cl_owner_id = str;
+       return 0;
 }
 
 static unsigned int
 
        /* nfs_client_id4 */
        nfs4_init_boot_verifier(clp, &sc_verifier);
-       if (test_bit(NFS_CS_MIGRATION, &clp->cl_flags))
+       if (test_bit(NFS_CS_MIGRATION, &clp->cl_flags)) {
                setclientid.sc_name_len =
                                nfs4_init_uniform_client_string(clp,
                                                setclientid.sc_name,
                                                sizeof(setclientid.sc_name));
-       else
-               setclientid.sc_name_len =
-                               nfs4_init_nonuniform_client_string(clp,
-                                               setclientid.sc_name,
-                                               sizeof(setclientid.sc_name));
-
-       if (!clp->cl_owner_id) {
-               status = -ENOMEM;
-               goto out;
+               if (!clp->cl_owner_id) {
+                       status = -ENOMEM;
+                       goto out;
+               }
+       } else {
+               status = nfs4_init_nonuniform_client_string(clp);
+               if (status)
+                       goto out;
        }
 
        /* cb_client4 */