+void
+init_nfsv4_state(struct nfs_server *server)
+{
+ server->nfs4_state = NULL;
+ INIT_LIST_HEAD(&server->nfs4_siblings);
+}
+
+void
+destroy_nfsv4_state(struct nfs_server *server)
+{
+ kfree(server->mnt_path);
+ server->mnt_path = NULL;
+ if (server->nfs4_state) {
+ nfs4_put_client(server->nfs4_state);
+ server->nfs4_state = NULL;
+ }
+}
+
+/*
+ * nfs4_get_client(): returns an empty client structure
+ * nfs4_put_client(): drops reference to client structure
+ *
+ * Since these are allocated/deallocated very rarely, we don't
+ * bother putting them in a slab cache...
+ */
+static struct nfs4_client *
+nfs4_alloc_client(struct in_addr *addr)
+{
+ struct nfs4_client *clp;
+
+ if (nfs_callback_up() < 0)
+ return NULL;
+ if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
+ nfs_callback_down();
+ return NULL;
+ }
+ memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
+ init_rwsem(&clp->cl_sem);
+ INIT_LIST_HEAD(&clp->cl_delegations);
+ INIT_LIST_HEAD(&clp->cl_state_owners);
+ INIT_LIST_HEAD(&clp->cl_unused);
+ spin_lock_init(&clp->cl_lock);
+ atomic_set(&clp->cl_count, 1);
+ INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
+ INIT_LIST_HEAD(&clp->cl_superblocks);
+ rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
+ clp->cl_rpcclient = ERR_PTR(-EINVAL);
+ clp->cl_boot_time = CURRENT_TIME;
+ clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
+ return clp;
+}
+
+static void
+nfs4_free_client(struct nfs4_client *clp)
+{
+ struct nfs4_state_owner *sp;
+
+ while (!list_empty(&clp->cl_unused)) {
+ sp = list_entry(clp->cl_unused.next,
+ struct nfs4_state_owner,
+ so_list);
+ list_del(&sp->so_list);
+ kfree(sp);
+ }
+ BUG_ON(!list_empty(&clp->cl_state_owners));
+ nfs_idmap_delete(clp);
+ if (!IS_ERR(clp->cl_rpcclient))
+ rpc_shutdown_client(clp->cl_rpcclient);
+ kfree(clp);
+ nfs_callback_down();
+}
+
+static struct nfs4_client *__nfs4_find_client(struct in_addr *addr)
+{
+ struct nfs4_client *clp;
+ list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
+ if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
+ atomic_inc(&clp->cl_count);
+ return clp;
+ }
+ }
+ return NULL;
+}
+
+struct nfs4_client *nfs4_find_client(struct in_addr *addr)
+{
+ struct nfs4_client *clp;
+ spin_lock(&state_spinlock);
+ clp = __nfs4_find_client(addr);
+ spin_unlock(&state_spinlock);
+ return clp;
+}
+
+struct nfs4_client *
+nfs4_get_client(struct in_addr *addr)
+{
+ struct nfs4_client *clp, *new = NULL;
+
+ spin_lock(&state_spinlock);
+ for (;;) {
+ clp = __nfs4_find_client(addr);
+ if (clp != NULL)
+ break;
+ clp = new;
+ if (clp != NULL) {
+ list_add(&clp->cl_servers, &nfs4_clientid_list);
+ new = NULL;
+ break;
+ }
+ spin_unlock(&state_spinlock);
+ new = nfs4_alloc_client(addr);
+ spin_lock(&state_spinlock);
+ if (new == NULL)
+ break;
+ }
+ spin_unlock(&state_spinlock);
+ if (new)
+ nfs4_free_client(new);
+ return clp;
+}
+
+void
+nfs4_put_client(struct nfs4_client *clp)
+{
+ if (!atomic_dec_and_lock(&clp->cl_count, &state_spinlock))
+ return;
+ list_del(&clp->cl_servers);
+ spin_unlock(&state_spinlock);
+ BUG_ON(!list_empty(&clp->cl_superblocks));
+ rpc_wake_up(&clp->cl_rpcwaitq);
+ nfs4_kill_renewd(clp);
+ nfs4_free_client(clp);
+}
+
+static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred)