NFS: Reduce the stack footprint of nfs_create_server
[safe/jmp/linux-2.6] / fs / nfs / client.c
index 4f75ec5..7ec9b34 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/vfs.h>
 #include <linux/inet.h>
 #include <linux/in6.h>
+#include <linux/slab.h>
 #include <net/ipv6.h>
 #include <linux/nfs_xdr.h>
 #include <linux/sunrpc/bc_xprt.h>
@@ -116,6 +117,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 {
        struct nfs_client *clp;
        struct rpc_cred *cred;
+       int err = -ENOMEM;
 
        if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
                goto error_0;
@@ -129,6 +131,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_addrlen = cl_init->addrlen;
 
        if (cl_init->hostname) {
+               err = -ENOMEM;
                clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
                if (!clp->cl_hostname)
                        goto error_cleanup;
@@ -159,33 +162,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 error_cleanup:
        kfree(clp);
 error_0:
-       return NULL;
+       return ERR_PTR(err);
 }
 
-static void nfs4_shutdown_client(struct nfs_client *clp)
-{
 #ifdef CONFIG_NFS_V4
-       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
-               nfs4_kill_renewd(clp);
-       BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners));
-       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
-               nfs_idmap_delete(clp);
-
-       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
-#endif
-}
-
-/*
- * Destroy the NFS4 callback service
- */
-static void nfs4_destroy_callback(struct nfs_client *clp)
-{
-#ifdef CONFIG_NFS_V4
-       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-               nfs_callback_down(clp->cl_minorversion);
-#endif /* CONFIG_NFS_V4 */
-}
-
 /*
  * Clears/puts all minor version specific parts from an nfs_client struct
  * reverting it to minorversion 0.
@@ -200,9 +180,33 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp)
 
        clp->cl_call_sync = _nfs4_call_sync;
 #endif /* CONFIG_NFS_V4_1 */
+}
 
+/*
+ * Destroy the NFS4 callback service
+ */
+static void nfs4_destroy_callback(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down(clp->cl_minorversion);
+}
+
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
+               nfs4_kill_renewd(clp);
+       nfs4_clear_client_minor_version(clp);
        nfs4_destroy_callback(clp);
+       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
+               nfs_idmap_delete(clp);
+
+       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
 }
+#else
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+}
+#endif /* CONFIG_NFS_V4 */
 
 /*
  * Destroy a shared client record
@@ -211,7 +215,6 @@ static void nfs_free_client(struct nfs_client *clp)
 {
        dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
 
-       nfs4_clear_client_minor_version(clp);
        nfs4_shutdown_client(clp);
 
        nfs_fscache_release_client_cookie(clp);
@@ -480,9 +483,10 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in
                spin_unlock(&nfs_client_lock);
 
                new = nfs_alloc_client(cl_init);
-       } while (new);
+       } while (!IS_ERR(new));
 
-       return ERR_PTR(-ENOMEM);
+       dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new));
+       return new;
 
        /* install a new client and return with it unready */
 install_client:
@@ -645,8 +649,6 @@ static int nfs_start_lockd(struct nfs_server *server)
                .hostname       = clp->cl_hostname,
                .address        = (struct sockaddr *)&clp->cl_addr,
                .addrlen        = clp->cl_addrlen,
-               .protocol       = server->flags & NFS_MOUNT_TCP ?
-                                               IPPROTO_TCP : IPPROTO_UDP,
                .nfs_version    = clp->rpc_ops->version,
                .noresvport     = server->flags & NFS_MOUNT_NORESVPORT ?
                                        1 : 0,
@@ -657,6 +659,14 @@ static int nfs_start_lockd(struct nfs_server *server)
        if (server->flags & NFS_MOUNT_NONLM)
                return 0;
 
+       switch (clp->cl_proto) {
+               default:
+                       nlm_init.protocol = IPPROTO_TCP;
+                       break;
+               case XPRT_TRANSPORT_UDP:
+                       nlm_init.protocol = IPPROTO_UDP;
+       }
+
        host = nlmclnt_init(&nlm_init);
        if (IS_ERR(host))
                return PTR_ERR(host);
@@ -784,7 +794,7 @@ static int nfs_init_server(struct nfs_server *server,
        dprintk("--> nfs_init_server()\n");
 
 #ifdef CONFIG_NFS_V3
-       if (data->flags & NFS_MOUNT_VER3)
+       if (data->version == 3)
                cl_init.rpc_ops = &nfs_v3_clientops;
 #endif
 
@@ -806,6 +816,9 @@ static int nfs_init_server(struct nfs_server *server,
        /* Initialise the client representation from the mount data */
        server->flags = data->flags;
        server->options = data->options;
+       server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
+               NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
+               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
 
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
@@ -876,6 +889,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
                server->rsize = NFS_MAX_FILE_IO_SIZE;
        server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 
+       server->backing_dev_info.name = "nfs";
        server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD;
 
        if (server->wsize > max_rpc_payload)
@@ -920,16 +934,11 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str
        }
 
        fsinfo.fattr = fattr;
-       nfs_fattr_init(fattr);
        error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
        if (error < 0)
                goto out_error;
 
        nfs_server_set_fsinfo(server, &fsinfo);
-       error = bdi_init(&server->backing_dev_info);
-       if (error)
-               goto out_error;
-
 
        /* Get some general file system info */
        if (server->namelen == 0) {
@@ -956,11 +965,14 @@ out_error:
 static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
 {
        target->flags = source->flags;
+       target->rsize = source->rsize;
+       target->wsize = source->wsize;
        target->acregmin = source->acregmin;
        target->acregmax = source->acregmax;
        target->acdirmin = source->acdirmin;
        target->acdirmax = source->acdirmax;
        target->caps = source->caps;
+       target->options = source->options;
 }
 
 /*
@@ -988,6 +1000,12 @@ static struct nfs_server *nfs_alloc_server(void)
                return NULL;
        }
 
+       if (bdi_init(&server->backing_dev_info)) {
+               nfs_free_iostats(server->io_stats);
+               kfree(server);
+               return NULL;
+       }
+
        return server;
 }
 
@@ -1028,13 +1046,18 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                                     struct nfs_fh *mntfh)
 {
        struct nfs_server *server;
-       struct nfs_fattr fattr;
+       struct nfs_fattr *fattr;
        int error;
 
        server = nfs_alloc_server();
        if (!server)
                return ERR_PTR(-ENOMEM);
 
+       error = -ENOMEM;
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               goto error;
+
        /* Get a client representation */
        error = nfs_init_server(server, data);
        if (error < 0)
@@ -1045,7 +1068,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 
        /* Probe the root fh to retrieve its FSID */
-       error = nfs_probe_fsinfo(server, mntfh, &fattr);
+       error = nfs_probe_fsinfo(server, mntfh, fattr);
        if (error < 0)
                goto error;
        if (server->nfs_client->rpc_ops->version == 3) {
@@ -1058,32 +1081,30 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                        server->namelen = NFS2_MAXNAMLEN;
        }
 
-       if (!(fattr.valid & NFS_ATTR_FATTR)) {
-               error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
+       if (!(fattr->valid & NFS_ATTR_FATTR)) {
+               error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr);
                if (error < 0) {
                        dprintk("nfs_create_server: getattr error = %d\n", -error);
                        goto error;
                }
        }
-       memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
+       memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
 
        dprintk("Server FSID: %llx:%llx\n",
                (unsigned long long) server->fsid.major,
                (unsigned long long) server->fsid.minor);
 
-       BUG_ON(!server->nfs_client);
-       BUG_ON(!server->nfs_client->rpc_ops);
-       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
-
        spin_lock(&nfs_client_lock);
        list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
        list_add_tail(&server->master_link, &nfs_volume_list);
        spin_unlock(&nfs_client_lock);
 
        server->mount_time = jiffies;
+       nfs_free_fattr(fattr);
        return server;
 
 error:
+       nfs_free_fattr(fattr);
        nfs_free_server(server);
        return ERR_PTR(error);
 }
@@ -1168,7 +1189,7 @@ static int nfs4_init_client(struct nfs_client *clp,
                                      1, flags & NFS_MOUNT_NORESVPORT);
        if (error < 0)
                goto error;
-       memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
+       strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
 
        error = nfs_idmap_new(clp);
        if (error < 0) {
@@ -1239,20 +1260,6 @@ error:
        return error;
 }
 
-/*
- * Initialize a session.
- * Note: save the mount rsize and wsize for create_server negotiation.
- */
-static void nfs4_init_session(struct nfs_client *clp,
-                             unsigned int wsize, unsigned int rsize)
-{
-#if defined(CONFIG_NFS_V4_1)
-       if (nfs4_has_session(clp)) {
-               clp->cl_session->fc_attrs.max_rqst_sz = wsize;
-               clp->cl_session->fc_attrs.max_resp_sz = rsize;
-       }
-#endif /* CONFIG_NFS_V4_1 */
-}
 
 /*
  * Session has been established, and the client marked ready.
@@ -1262,10 +1269,20 @@ static void nfs4_init_session(struct nfs_client *clp,
 static void nfs4_session_set_rwsize(struct nfs_server *server)
 {
 #ifdef CONFIG_NFS_V4_1
+       struct nfs4_session *sess;
+       u32 server_resp_sz;
+       u32 server_rqst_sz;
+
        if (!nfs4_has_session(server->nfs_client))
                return;
-       server->rsize = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
-       server->wsize = server->nfs_client->cl_session->fc_attrs.max_rqst_sz;
+       sess = server->nfs_client->cl_session;
+       server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
+       server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
+
+       if (server->rsize > server_resp_sz)
+               server->rsize = server_resp_sz;
+       if (server->wsize > server_rqst_sz)
+               server->wsize = server_rqst_sz;
 #endif /* CONFIG_NFS_V4_1 */
 }
 
@@ -1285,7 +1302,8 @@ static int nfs4_init_server(struct nfs_server *server,
 
        /* Initialise the client representation from the mount data */
        server->flags = data->flags;
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|
+               NFS_CAP_POSIX_LOCK;
        server->options = data->options;
 
        /* Get a client record */
@@ -1328,7 +1346,7 @@ error:
 struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
                                      struct nfs_fh *mntfh)
 {
-       struct nfs_fattr fattr;
+       struct nfs_fattr *fattr;
        struct nfs_server *server;
        int error;
 
@@ -1338,6 +1356,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
        if (!server)
                return ERR_PTR(-ENOMEM);
 
+       error = -ENOMEM;
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               goto error;
+
        /* set up the general RPC client */
        error = nfs4_init_server(server, data);
        if (error < 0)
@@ -1347,10 +1370,12 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
        BUG_ON(!server->nfs_client->rpc_ops);
        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 
-       nfs4_init_session(server->nfs_client, server->wsize, server->rsize);
+       error = nfs4_init_session(server);
+       if (error < 0)
+               goto error;
 
        /* Probe the root fh to retrieve its FSID */
-       error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path);
+       error = nfs4_get_rootfh(server, mntfh);
        if (error < 0)
                goto error;
 
@@ -1361,17 +1386,13 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
 
        nfs4_session_set_rwsize(server);
 
-       error = nfs_probe_fsinfo(server, mntfh, &fattr);
+       error = nfs_probe_fsinfo(server, mntfh, fattr);
        if (error < 0)
                goto error;
 
        if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
                server->namelen = NFS4_MAXNAMLEN;
 
-       BUG_ON(!server->nfs_client);
-       BUG_ON(!server->nfs_client->rpc_ops);
-       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
-
        spin_lock(&nfs_client_lock);
        list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
        list_add_tail(&server->master_link, &nfs_volume_list);
@@ -1379,9 +1400,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
 
        server->mount_time = jiffies;
        dprintk("<-- nfs4_create_server() = %p\n", server);
+       nfs_free_fattr(fattr);
        return server;
 
 error:
+       nfs_free_fattr(fattr);
        nfs_free_server(server);
        dprintk("<-- nfs4_create_server() = error %d\n", error);
        return ERR_PTR(error);
@@ -1395,7 +1418,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 {
        struct nfs_client *parent_client;
        struct nfs_server *server, *parent_server;
-       struct nfs_fattr fattr;
+       struct nfs_fattr *fattr;
        int error;
 
        dprintk("--> nfs4_create_referral_server()\n");
@@ -1404,12 +1427,17 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
        if (!server)
                return ERR_PTR(-ENOMEM);
 
+       error = -ENOMEM;
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               goto error;
+
        parent_server = NFS_SB(data->sb);
        parent_client = parent_server->nfs_client;
 
        /* Initialise the client representation from the parent server */
        nfs_server_copy_userdata(server, parent_server);
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
 
        /* Get a client representation.
         * Note: NFSv4 always uses TCP, */
@@ -1433,12 +1461,12 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 
        /* Probe the root fh to retrieve its FSID and filehandle */
-       error = nfs4_path_walk(server, mntfh, data->mnt_path);
+       error = nfs4_get_rootfh(server, mntfh);
        if (error < 0)
                goto error;
 
        /* probe the filesystem info for this server filesystem */
-       error = nfs_probe_fsinfo(server, mntfh, &fattr);
+       error = nfs_probe_fsinfo(server, mntfh, fattr);
        if (error < 0)
                goto error;
 
@@ -1456,10 +1484,12 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 
        server->mount_time = jiffies;
 
+       nfs_free_fattr(fattr);
        dprintk("<-- nfs_create_referral_server() = %p\n", server);
        return server;
 
 error:
+       nfs_free_fattr(fattr);
        nfs_free_server(server);
        dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
        return ERR_PTR(error);
@@ -1475,7 +1505,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
                                    struct nfs_fattr *fattr)
 {
        struct nfs_server *server;
-       struct nfs_fattr fattr_fsinfo;
+       struct nfs_fattr *fattr_fsinfo;
        int error;
 
        dprintk("--> nfs_clone_server(,%llx:%llx,)\n",
@@ -1486,6 +1516,11 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
        if (!server)
                return ERR_PTR(-ENOMEM);
 
+       error = -ENOMEM;
+       fattr_fsinfo = nfs_alloc_fattr();
+       if (fattr_fsinfo == NULL)
+               goto out_free_server;
+
        /* Copy data from the source */
        server->nfs_client = source->nfs_client;
        atomic_inc(&server->nfs_client->cl_count);
@@ -1502,7 +1537,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
                nfs_init_server_aclclient(server);
 
        /* probe the filesystem info for this server filesystem */
-       error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo);
+       error = nfs_probe_fsinfo(server, fh, fattr_fsinfo);
        if (error < 0)
                goto out_free_server;
 
@@ -1524,10 +1559,12 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
 
        server->mount_time = jiffies;
 
+       nfs_free_fattr(fattr_fsinfo);
        dprintk("<-- nfs_clone_server() = %p\n", server);
        return server;
 
 out_free_server:
+       nfs_free_fattr(fattr_fsinfo);
        nfs_free_server(server);
        dprintk("<-- nfs_clone_server() = error %d\n", error);
        return ERR_PTR(error);
@@ -1542,7 +1579,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos);
 static void nfs_server_list_stop(struct seq_file *p, void *v);
 static int nfs_server_list_show(struct seq_file *m, void *v);
 
-static struct seq_operations nfs_server_list_ops = {
+static const struct seq_operations nfs_server_list_ops = {
        .start  = nfs_server_list_start,
        .next   = nfs_server_list_next,
        .stop   = nfs_server_list_stop,
@@ -1563,7 +1600,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos);
 static void nfs_volume_list_stop(struct seq_file *p, void *v);
 static int nfs_volume_list_show(struct seq_file *m, void *v);
 
-static struct seq_operations nfs_volume_list_ops = {
+static const struct seq_operations nfs_volume_list_ops = {
        .start  = nfs_volume_list_start,
        .next   = nfs_volume_list_next,
        .stop   = nfs_volume_list_stop,