NFSv4.1: data server connection
Andy Adamson [Tue, 1 Mar 2011 01:34:17 +0000 (01:34 +0000)]
Introduce a data server set_client and init session following the
nfs4_set_client and  nfs4_init_session convention.

Once a new nfs_client is on the nfs_client_list, the nfs_client cl_cons_state
serializes access to creating an nfs_client struct with matching properties.

Use the new nfs_get_client() that initializes new clients.

Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

fs/nfs/client.c
fs/nfs/internal.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4filelayoutdev.c
fs/nfs/nfs4proc.c
include/linux/nfs_xdr.h

index d5c5bdf..6dd50ac 100644 (file)
@@ -1417,6 +1417,47 @@ error:
        return error;
 }
 
+/*
+ * Set up a pNFS Data Server client.
+ *
+ * Return any existing nfs_client that matches server address,port,version
+ * and minorversion.
+ *
+ * For a new nfs_client, use a soft mount (default), a low retrans and a
+ * low timeout interval so that if a connection is lost, we retry through
+ * the MDS.
+ */
+struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
+               const struct sockaddr *ds_addr,
+               int ds_addrlen, int ds_proto)
+{
+       struct nfs_client_initdata cl_init = {
+               .addr = ds_addr,
+               .addrlen = ds_addrlen,
+               .rpc_ops = &nfs_v4_clientops,
+               .proto = ds_proto,
+               .minorversion = mds_clp->cl_minorversion,
+       };
+       struct rpc_timeout ds_timeout = {
+               .to_initval = 15 * HZ,
+               .to_maxval = 15 * HZ,
+               .to_retries = 1,
+               .to_exponential = 1,
+       };
+       struct nfs_client *clp;
+
+       /*
+        * Set an authflavor equual to the MDS value. Use the MDS nfs_client
+        * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
+        * (section 13.1 RFC 5661).
+        */
+       clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
+                            mds_clp->cl_rpcclient->cl_auth->au_flavor, 0);
+
+       dprintk("<-- %s %p\n", __func__, clp);
+       return clp;
+}
+EXPORT_SYMBOL(nfs4_set_ds_client);
 
 /*
  * Session has been established, and the client marked ready.
index 4d7b3a9..5cc9201 100644 (file)
@@ -148,6 +148,9 @@ extern struct nfs_server *nfs_clone_server(struct nfs_server *,
                                           struct nfs_fattr *);
 extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
 extern int nfs4_check_client_ready(struct nfs_client *clp);
+extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
+                                            const struct sockaddr *ds_addr,
+                                            int ds_addrlen, int ds_proto);
 #ifdef CONFIG_PROC_FS
 extern int __init nfs_fs_proc_init(void);
 extern void nfs_fs_proc_exit(void);
@@ -213,6 +216,8 @@ extern const u32 nfs41_maxwrite_overhead;
 extern struct rpc_procinfo nfs4_procedures[];
 #endif
 
+extern int nfs4_init_ds_session(struct nfs_client *clp);
+
 /* proc.c */
 void nfs_close_context(struct nfs_open_context *ctx, int is_sync);
 extern int nfs_init_client(struct nfs_client *clp,
index d4cfacc..7058a9f 100644 (file)
@@ -266,6 +266,12 @@ is_ds_only_client(struct nfs_client *clp)
        return (clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) ==
                EXCHGID4_FLAG_USE_PNFS_DS;
 }
+
+static inline bool
+is_ds_client(struct nfs_client *clp)
+{
+       return clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_DS;
+}
 #else /* CONFIG_NFS_v4_1 */
 static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
 {
@@ -289,6 +295,12 @@ is_ds_only_client(struct nfs_client *clp)
 {
        return false;
 }
+
+static inline bool
+is_ds_client(struct nfs_client *clp)
+{
+       return false;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[];
index b73c343..8bc91fb 100644 (file)
@@ -104,6 +104,67 @@ _data_server_lookup_locked(u32 ip_addr, u32 port)
        return NULL;
 }
 
+/*
+ * Create an rpc connection to the nfs4_pnfs_ds data server
+ * Currently only support IPv4
+ */
+static int
+nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
+{
+       struct nfs_client *clp;
+       struct sockaddr_in sin;
+       int status = 0;
+
+       dprintk("--> %s ip:port %x:%hu au_flavor %d\n", __func__,
+               ntohl(ds->ds_ip_addr), ntohs(ds->ds_port),
+               mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor);
+
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = ds->ds_ip_addr;
+       sin.sin_port = ds->ds_port;
+
+       clp = nfs4_set_ds_client(mds_srv->nfs_client, (struct sockaddr *)&sin,
+                                sizeof(sin), IPPROTO_TCP);
+       if (IS_ERR(clp)) {
+               status = PTR_ERR(clp);
+               goto out;
+       }
+
+       if ((clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) != 0) {
+               if (!is_ds_client(clp)) {
+                       status = -ENODEV;
+                       goto out_put;
+               }
+               ds->ds_clp = clp;
+               dprintk("%s [existing] ip=%x, port=%hu\n", __func__,
+                       ntohl(ds->ds_ip_addr), ntohs(ds->ds_port));
+               goto out;
+       }
+
+       /*
+        * Do not set NFS_CS_CHECK_LEASE_TIME instead set the DS lease to
+        * be equal to the MDS lease. Renewal is scheduled in create_session.
+        */
+       spin_lock(&mds_srv->nfs_client->cl_lock);
+       clp->cl_lease_time = mds_srv->nfs_client->cl_lease_time;
+       spin_unlock(&mds_srv->nfs_client->cl_lock);
+       clp->cl_last_renewal = jiffies;
+
+       /* New nfs_client */
+       status = nfs4_init_ds_session(clp);
+       if (status)
+               goto out_put;
+
+       ds->ds_clp = clp;
+       dprintk("%s [new] ip=%x, port=%hu\n", __func__, ntohl(ds->ds_ip_addr),
+               ntohs(ds->ds_port));
+out:
+       return status;
+out_put:
+       nfs_put_client(clp);
+       goto out;
+}
+
 static void
 destroy_ds(struct nfs4_pnfs_ds *ds)
 {
index 55a8fc2..07d1a43 100644 (file)
@@ -1573,9 +1573,8 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
        return 0;
 }
 
-static int nfs4_recover_expired_lease(struct nfs_server *server)
+static int nfs4_client_recover_expired_lease(struct nfs_client *clp)
 {
-       struct nfs_client *clp = server->nfs_client;
        unsigned int loop;
        int ret;
 
@@ -1592,6 +1591,11 @@ static int nfs4_recover_expired_lease(struct nfs_server *server)
        return ret;
 }
 
+static int nfs4_recover_expired_lease(struct nfs_server *server)
+{
+       return nfs4_client_recover_expired_lease(server->nfs_client);
+}
+
 /*
  * OPEN_EXPIRED:
  *     reclaim state on the server after a network partition.
@@ -5118,6 +5122,27 @@ int nfs4_init_session(struct nfs_server *server)
        return ret;
 }
 
+int nfs4_init_ds_session(struct nfs_client *clp)
+{
+       struct nfs4_session *session = clp->cl_session;
+       int ret;
+
+       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
+               return 0;
+
+       ret = nfs4_client_recover_expired_lease(clp);
+       if (!ret)
+               /* Test for the DS role */
+               if (!is_ds_client(clp))
+                       ret = -ENODEV;
+       if (!ret)
+               ret = nfs4_check_client_ready(clp);
+       return ret;
+
+}
+EXPORT_SYMBOL_GPL(nfs4_init_ds_session);
+
+
 /*
  * Renew the cl_session lease.
  */
index 9d2b9da..c66ff7f 100644 (file)
@@ -1018,6 +1018,7 @@ struct nfs_read_data {
        struct nfs_readres  res;
        unsigned long           timestamp;      /* For lease renewal */
        struct pnfs_layout_segment *lseg;
+       struct nfs_client       *ds_clp;        /* pNFS data server */
        const struct rpc_call_ops *mds_ops;
        struct page             *page_array[NFS_PAGEVEC_SIZE];
 };