blob: c5234b589907e52013946e356ec41744065bc31e [file] [log] [blame]
Bryan Schumaker428360d2012-07-16 16:39:17 -04001/*
2 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
3 * Written by David Howells (dhowells@redhat.com)
4 */
5#include <linux/nfs_fs.h>
6#include <linux/nfs_idmap.h>
7#include <linux/sunrpc/auth.h>
8#include <linux/sunrpc/xprt.h>
9#include <linux/sunrpc/bc_xprt.h>
10#include "internal.h"
11#include "callback.h"
12
13#define NFSDBG_FACILITY NFSDBG_CLIENT
14
15/*
16 * Initialize the NFS4 callback service
17 */
18static int nfs4_init_callback(struct nfs_client *clp)
19{
20 int error;
21
22 if (clp->rpc_ops->version == 4) {
23 struct rpc_xprt *xprt;
24
25 xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
26
27 if (nfs4_has_session(clp)) {
28 error = xprt_setup_backchannel(xprt,
29 NFS41_BC_MIN_CALLBACKS);
30 if (error < 0)
31 return error;
32 }
33
34 error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
35 if (error < 0) {
36 dprintk("%s: failed to start callback. Error = %d\n",
37 __func__, error);
38 return error;
39 }
40 __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
41 }
42 return 0;
43}
44
45/*
46 * Initialize the minor version specific parts of an NFS4 client record
47 */
48static int nfs4_init_client_minor_version(struct nfs_client *clp)
49{
50#if defined(CONFIG_NFS_V4_1)
51 if (clp->cl_mvops->minor_version) {
52 struct nfs4_session *session = NULL;
53 /*
54 * Create the session and mark it expired.
55 * When a SEQUENCE operation encounters the expired session
56 * it will do session recovery to initialize it.
57 */
58 session = nfs4_alloc_session(clp);
59 if (!session)
60 return -ENOMEM;
61
62 clp->cl_session = session;
63 /*
64 * The create session reply races with the server back
65 * channel probe. Mark the client NFS_CS_SESSION_INITING
66 * so that the client back channel can find the
67 * nfs_client struct
68 */
69 nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
70 }
71#endif /* CONFIG_NFS_V4_1 */
72
73 return nfs4_init_callback(clp);
74}
75
76/**
77 * nfs4_init_client - Initialise an NFS4 client record
78 *
79 * @clp: nfs_client to initialise
80 * @timeparms: timeout parameters for underlying RPC transport
81 * @ip_addr: callback IP address in presentation format
82 * @authflavor: authentication flavor for underlying RPC transport
83 *
84 * Returns pointer to an NFS client, or an ERR_PTR value.
85 */
86struct nfs_client *nfs4_init_client(struct nfs_client *clp,
87 const struct rpc_timeout *timeparms,
88 const char *ip_addr,
89 rpc_authflavor_t authflavour)
90{
91 char buf[INET6_ADDRSTRLEN + 1];
92 int error;
93
94 if (clp->cl_cons_state == NFS_CS_READY) {
95 /* the client is initialised already */
96 dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
97 return clp;
98 }
99
100 /* Check NFS protocol revision and initialize RPC op vector */
101 clp->rpc_ops = &nfs_v4_clientops;
102
103 __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
104 error = nfs_create_rpc_client(clp, timeparms, authflavour);
105 if (error < 0)
106 goto error;
107
108 /* If no clientaddr= option was specified, find a usable cb address */
109 if (ip_addr == NULL) {
110 struct sockaddr_storage cb_addr;
111 struct sockaddr *sap = (struct sockaddr *)&cb_addr;
112
113 error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
114 if (error < 0)
115 goto error;
116 error = rpc_ntop(sap, buf, sizeof(buf));
117 if (error < 0)
118 goto error;
119 ip_addr = (const char *)buf;
120 }
121 strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
122
123 error = nfs_idmap_new(clp);
124 if (error < 0) {
125 dprintk("%s: failed to create idmapper. Error = %d\n",
126 __func__, error);
127 goto error;
128 }
129 __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
130
131 error = nfs4_init_client_minor_version(clp);
132 if (error < 0)
133 goto error;
134
135 if (!nfs4_has_session(clp))
136 nfs_mark_client_ready(clp, NFS_CS_READY);
137 return clp;
138
139error:
140 nfs_mark_client_ready(clp, error);
141 nfs_put_client(clp);
142 dprintk("<-- nfs4_init_client() = xerror %d\n", error);
143 return ERR_PTR(error);
144}