nfsd41: bound forechannel drc size by memory usage
[safe/jmp/linux-2.6] / fs / nfsd / nfs4state.c
index ef6944b..02b3ddd 100644 (file)
@@ -55,6 +55,7 @@
 #include <linux/lockd/bind.h>
 #include <linux/module.h>
 #include <linux/sunrpc/svcauth_gss.h>
+#include <linux/sunrpc/clnt.h>
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
@@ -413,36 +414,65 @@ gen_sessionid(struct nfsd4_session *ses)
 }
 
 /*
- * Give the client the number of slots it requests bound by
- * NFSD_MAX_SLOTS_PER_SESSION and by sv_drc_max_pages.
+ * The protocol defines ca_maxresponssize_cached to include the size of
+ * the rpc header, but all we need to cache is the data starting after
+ * the end of the initial SEQUENCE operation--the rest we regenerate
+ * each time.  Therefore we can advertise a ca_maxresponssize_cached
+ * value that is the number of bytes in our cache plus a few additional
+ * bytes.  In order to stay on the safe side, and not promise more than
+ * we can cache, those additional bytes must be the minimum possible: 24
+ * bytes of rpc header (xid through accept state, with AUTH_NULL
+ * verifier), 12 for the compound header (with zero-length tag), and 44
+ * for the SEQUENCE op response:
+ */
+#define NFSD_MIN_HDR_SEQ_SZ  (24 + 12 + 44)
+
+/*
+ * Give the client the number of ca_maxresponsesize_cached slots it
+ * requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
+ * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
+ * than NFSD_MAX_SLOTS_PER_SESSION.
  *
- * If we run out of pages (sv_drc_pages_used == sv_drc_max_pages) we
- * should (up to a point) re-negotiate active sessions and reduce their
- * slot usage to make rooom for new connections. For now we just fail the
- * create session.
+ * If we run out of reserved DRC memory we should (up to a point)
+ * re-negotiate active sessions and reduce their slot usage to make
+ * rooom for new connections. For now we just fail the create session.
  */
-static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan)
+static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
 {
-       int status = 0, np = fchan->maxreqs * NFSD_PAGES_PER_SLOT;
+       int mem, size = fchan->maxresp_cached;
 
        if (fchan->maxreqs < 1)
                return nfserr_inval;
-       else if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
-               fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
 
-       spin_lock(&nfsd_serv->sv_lock);
-       if (np + nfsd_serv->sv_drc_pages_used > nfsd_serv->sv_drc_max_pages)
-               np = nfsd_serv->sv_drc_max_pages - nfsd_serv->sv_drc_pages_used;
-       nfsd_serv->sv_drc_pages_used += np;
-       spin_unlock(&nfsd_serv->sv_lock);
+       if (size < NFSD_MIN_HDR_SEQ_SZ)
+               size = NFSD_MIN_HDR_SEQ_SZ;
+       size -= NFSD_MIN_HDR_SEQ_SZ;
+       if (size > NFSD_SLOT_CACHE_SIZE)
+               size = NFSD_SLOT_CACHE_SIZE;
+
+       /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
+       mem = fchan->maxreqs * size;
+       if (mem > NFSD_MAX_MEM_PER_SESSION) {
+               fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
+               if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
+                       fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
+               mem = fchan->maxreqs * size;
+       }
 
-       if (np <= 0) {
-               status = nfserr_resource;
-               fchan->maxreqs = 0;
-       } else
-               fchan->maxreqs = np / NFSD_PAGES_PER_SLOT;
+       spin_lock(&nfsd_drc_lock);
+       /* bound the total session drc memory ussage */
+       if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
+               fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
+               mem = fchan->maxreqs * size;
+       }
+       nfsd_drc_mem_used += mem;
+       spin_unlock(&nfsd_drc_lock);
 
-       return status;
+       if (fchan->maxreqs == 0)
+               return nfserr_resource;
+
+       fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
+       return 0;
 }
 
 /*
@@ -466,27 +496,21 @@ static int init_forechannel_attrs(struct svc_rqst *rqstp,
                fchan->maxresp_sz = maxcount;
        session_fchan->maxresp_sz = fchan->maxresp_sz;
 
-       /* Set the max response cached size our default which is
-        * a multiple of PAGE_SIZE and small */
-       session_fchan->maxresp_cached = NFSD_PAGES_PER_SLOT * PAGE_SIZE;
-       fchan->maxresp_cached = session_fchan->maxresp_cached;
-
        /* Use the client's maxops if possible */
        if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
                fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
        session_fchan->maxops = fchan->maxops;
 
-       /* try to use the client requested number of slots */
-       if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
-               fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
-
        /* FIXME: Error means no more DRC pages so the server should
         * recover pages from existing sessions. For now fail session
         * creation.
         */
-       status = set_forechannel_maxreqs(fchan);
+       status = set_forechannel_drc_size(fchan);
 
+       session_fchan->maxresp_cached = fchan->maxresp_cached;
        session_fchan->maxreqs = fchan->maxreqs;
+
+       dprintk("%s status %d\n", __func__, status);
        return status;
 }
 
@@ -585,6 +609,9 @@ free_session(struct kref *kref)
                struct nfsd4_cache_entry *e = &ses->se_slots[i].sl_cache_entry;
                nfsd4_release_respages(e->ce_respages, e->ce_resused);
        }
+       spin_lock(&nfsd_drc_lock);
+       nfsd_drc_mem_used -= ses->se_fchannel.maxreqs * NFSD_SLOT_CACHE_SIZE;
+       spin_unlock(&nfsd_drc_lock);
        kfree(ses);
 }
 
@@ -657,8 +684,6 @@ static inline void
 free_client(struct nfs4_client *clp)
 {
        shutdown_callback_client(clp);
-       nfsd4_release_respages(clp->cl_slot.sl_cache_entry.ce_respages,
-                            clp->cl_slot.sl_cache_entry.ce_resused);
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
        kfree(clp->cl_principal);
@@ -902,92 +927,40 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
        return NULL;
 }
 
-/* a helper function for parse_callback */
-static int
-parse_octet(unsigned int *lenp, char **addrp)
-{
-       unsigned int len = *lenp;
-       char *p = *addrp;
-       int n = -1;
-       char c;
-
-       for (;;) {
-               if (!len)
-                       break;
-               len--;
-               c = *p++;
-               if (c == '.')
-                       break;
-               if ((c < '0') || (c > '9')) {
-                       n = -1;
-                       break;
-               }
-               if (n < 0)
-                       n = 0;
-               n = (n * 10) + (c - '0');
-               if (n > 255) {
-                       n = -1;
-                       break;
-               }
-       }
-       *lenp = len;
-       *addrp = p;
-       return n;
-}
-
-/* parse and set the setclientid ipv4 callback address */
-static int
-parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp)
-{
-       int temp = 0;
-       u32 cbaddr = 0;
-       u16 cbport = 0;
-       u32 addrlen = addr_len;
-       char *addr = addr_val;
-       int i, shift;
-
-       /* ipaddress */
-       shift = 24;
-       for(i = 4; i > 0  ; i--) {
-               if ((temp = parse_octet(&addrlen, &addr)) < 0) {
-                       return 0;
-               }
-               cbaddr |= (temp << shift);
-               if (shift > 0)
-               shift -= 8;
-       }
-       *cbaddrp = cbaddr;
-
-       /* port */
-       shift = 8;
-       for(i = 2; i > 0  ; i--) {
-               if ((temp = parse_octet(&addrlen, &addr)) < 0) {
-                       return 0;
-               }
-               cbport |= (temp << shift);
-               if (shift > 0)
-                       shift -= 8;
-       }
-       *cbportp = cbport;
-       return 1;
-}
-
 static void
-gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
+gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
 {
        struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
-
-       /* Currently, we only support tcp for the callback channel */
-       if ((se->se_callback_netid_len != 3) || memcmp((char *)se->se_callback_netid_val, "tcp", 3))
+       unsigned short expected_family;
+
+       /* Currently, we only support tcp and tcp6 for the callback channel */
+       if (se->se_callback_netid_len == 3 &&
+           !memcmp(se->se_callback_netid_val, "tcp", 3))
+               expected_family = AF_INET;
+       else if (se->se_callback_netid_len == 4 &&
+                !memcmp(se->se_callback_netid_val, "tcp6", 4))
+               expected_family = AF_INET6;
+       else
                goto out_err;
 
-       if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val,
-                        &cb->cb_addr, &cb->cb_port)))
+       cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
+                                           se->se_callback_addr_len,
+                                           (struct sockaddr *) &cb->cb_addr,
+                                           sizeof(cb->cb_addr));
+
+       if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
                goto out_err;
+
+       if (cb->cb_addr.ss_family == AF_INET6)
+               ((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
+
+       cb->cb_minorversion = 0;
        cb->cb_prog = se->se_callback_prog;
        cb->cb_ident = se->se_callback_ident;
        return;
 out_err:
+       cb->cb_addr.ss_family = AF_UNSPEC;
+       cb->cb_addrlen = 0;
        dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
                "will not receive delegations\n",
                clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
@@ -1048,16 +1021,10 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
 {
        struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry;
        struct svc_rqst *rqstp = resp->rqstp;
-       struct nfsd4_compoundargs *args = rqstp->rq_argp;
-       struct nfsd4_op *op = &args->ops[resp->opcnt];
        struct kvec *resv = &rqstp->rq_res.head[0];
 
        dprintk("--> %s entry %p\n", __func__, entry);
 
-       /* Don't cache a failed OP_SEQUENCE. */
-       if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status)
-               return;
-
        nfsd4_release_respages(entry->ce_respages, entry->ce_resused);
        entry->ce_opcnt = resp->opcnt;
        entry->ce_status = resp->cstate.status;
@@ -1114,6 +1081,36 @@ nfsd41_copy_replay_data(struct nfsd4_compoundres *resp,
 }
 
 /*
+ * Encode the replay sequence operation from the slot values.
+ * If cachethis is FALSE encode the uncached rep error on the next
+ * operation which sets resp->p and increments resp->opcnt for
+ * nfs4svc_encode_compoundres.
+ *
+ */
+static __be32
+nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args,
+                         struct nfsd4_compoundres *resp)
+{
+       struct nfsd4_op *op;
+       struct nfsd4_slot *slot = resp->cstate.slot;
+
+       dprintk("--> %s resp->opcnt %d cachethis %u \n", __func__,
+               resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);
+
+       /* Encode the replayed sequence operation */
+       op = &args->ops[resp->opcnt - 1];
+       nfsd4_encode_operation(resp, op);
+
+       /* Return nfserr_retry_uncached_rep in next operation. */
+       if (args->opcnt > 1 && slot->sl_cache_entry.ce_cachethis == 0) {
+               op = &args->ops[resp->opcnt++];
+               op->status = nfserr_retry_uncached_rep;
+               nfsd4_encode_operation(resp, op);
+       }
+       return op->status;
+}
+
+/*
  * Keep the first page of the replay. Copy the NFSv4.1 data from the first
  * cached page.  Replace any futher replay pages from the cache.
  */
@@ -1136,10 +1133,12 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
         * session inactivity timer fires and a solo sequence operation
         * is sent (lease renewal).
         */
-       if (seq && nfsd4_not_cached(resp)) {
-               seq->maxslots = resp->cstate.session->se_fchannel.maxreqs;
-               return nfs_ok;
-       }
+       seq->maxslots = resp->cstate.session->se_fchannel.maxreqs;
+
+       /* Either returns 0 or nfserr_retry_uncached */
+       status = nfsd4_enc_sequence_replay(resp->rqstp->rq_argp, resp);
+       if (status == nfserr_retry_uncached_rep)
+               return status;
 
        if (!nfsd41_copy_replay_data(resp, entry)) {
                /*
@@ -1193,13 +1192,15 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
        int status;
        unsigned int            strhashval;
        char                    dname[HEXDIR_LEN];
+       char                    addr_str[INET6_ADDRSTRLEN];
        nfs4_verifier           verf = exid->verifier;
-       u32                     ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr;
+       struct sockaddr         *sa = svc_addr(rqstp);
 
+       rpc_ntop(sa, addr_str, sizeof(addr_str));
        dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p "
-               " ip_addr=%u flags %x, spa_how %d\n",
+               "ip_addr=%s flags %x, spa_how %d\n",
                __func__, rqstp, exid, exid->clname.len, exid->clname.data,
-               ip_addr, exid->flags, exid->spa_how);
+               addr_str, exid->flags, exid->spa_how);
 
        if (!check_name(exid->clname) || (exid->flags & ~EXCHGID4_FLAG_MASK_A))
                return nfserr_inval;
@@ -1288,7 +1289,7 @@ out_new:
 
        copy_verf(new, &verf);
        copy_cred(&new->cl_cred, &rqstp->rq_cred);
-       new->cl_addr = ip_addr;
+       rpc_copy_addr((struct sockaddr *) &new->cl_addr, sa);
        gen_clid(new);
        gen_confirm(new);
        add_to_unconfirmed(new, strhashval);
@@ -1296,12 +1297,11 @@ out_copy:
        exid->clientid.cl_boot = new->cl_clientid.cl_boot;
        exid->clientid.cl_id = new->cl_clientid.cl_id;
 
-       new->cl_slot.sl_seqid = 0;
        exid->seqid = 1;
        nfsd4_set_ex_flags(new, exid);
 
        dprintk("nfsd4_exchange_id seqid %d flags %x\n",
-               new->cl_slot.sl_seqid, new->cl_exchange_flags);
+               new->cl_cs_slot.sl_seqid, new->cl_exchange_flags);
        status = nfs_ok;
 
 out:
@@ -1312,40 +1312,60 @@ error:
 }
 
 static int
-check_slot_seqid(u32 seqid, struct nfsd4_slot *slot)
+check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse)
 {
-       dprintk("%s enter. seqid %d slot->sl_seqid %d\n", __func__, seqid,
-               slot->sl_seqid);
+       dprintk("%s enter. seqid %d slot_seqid %d\n", __func__, seqid,
+               slot_seqid);
 
        /* The slot is in use, and no response has been sent. */
-       if (slot->sl_inuse) {
-               if (seqid == slot->sl_seqid)
+       if (slot_inuse) {
+               if (seqid == slot_seqid)
                        return nfserr_jukebox;
                else
                        return nfserr_seq_misordered;
        }
        /* Normal */
-       if (likely(seqid == slot->sl_seqid + 1))
+       if (likely(seqid == slot_seqid + 1))
                return nfs_ok;
        /* Replay */
-       if (seqid == slot->sl_seqid)
+       if (seqid == slot_seqid)
                return nfserr_replay_cache;
        /* Wraparound */
-       if (seqid == 1 && (slot->sl_seqid + 1) == 0)
+       if (seqid == 1 && (slot_seqid + 1) == 0)
                return nfs_ok;
        /* Misordered replay or misordered new request */
        return nfserr_seq_misordered;
 }
 
+/*
+ * Cache the create session result into the create session single DRC
+ * slot cache by saving the xdr structure. sl_seqid has been set.
+ * Do this for solo or embedded create session operations.
+ */
+static void
+nfsd4_cache_create_session(struct nfsd4_create_session *cr_ses,
+                          struct nfsd4_clid_slot *slot, int nfserr)
+{
+       slot->sl_status = nfserr;
+       memcpy(&slot->sl_cr_ses, cr_ses, sizeof(*cr_ses));
+}
+
+static __be32
+nfsd4_replay_create_session(struct nfsd4_create_session *cr_ses,
+                           struct nfsd4_clid_slot *slot)
+{
+       memcpy(cr_ses, &slot->sl_cr_ses, sizeof(*cr_ses));
+       return slot->sl_status;
+}
+
 __be32
 nfsd4_create_session(struct svc_rqst *rqstp,
                     struct nfsd4_compound_state *cstate,
                     struct nfsd4_create_session *cr_ses)
 {
-       u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr;
-       struct nfsd4_compoundres *resp = rqstp->rq_resp;
+       struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
-       struct nfsd4_slot *slot = NULL;
+       struct nfsd4_clid_slot *cs_slot = NULL;
        int status = 0;
 
        nfs4_lock_state();
@@ -1353,40 +1373,38 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        conf = find_confirmed_client(&cr_ses->clientid);
 
        if (conf) {
-               slot = &conf->cl_slot;
-               status = check_slot_seqid(cr_ses->seqid, slot);
+               cs_slot = &conf->cl_cs_slot;
+               status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status == nfserr_replay_cache) {
                        dprintk("Got a create_session replay! seqid= %d\n",
-                               slot->sl_seqid);
-                       cstate->slot = slot;
-                       cstate->status = status;
+                               cs_slot->sl_seqid);
                        /* Return the cached reply status */
-                       status = nfsd4_replay_cache_entry(resp, NULL);
+                       status = nfsd4_replay_create_session(cr_ses, cs_slot);
                        goto out;
-               } else if (cr_ses->seqid != conf->cl_slot.sl_seqid + 1) {
+               } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
                        status = nfserr_seq_misordered;
                        dprintk("Sequence misordered!\n");
                        dprintk("Expected seqid= %d but got seqid= %d\n",
-                               slot->sl_seqid, cr_ses->seqid);
+                               cs_slot->sl_seqid, cr_ses->seqid);
                        goto out;
                }
-               conf->cl_slot.sl_seqid++;
+               cs_slot->sl_seqid++;
        } else if (unconf) {
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
-                   (ip_addr != unconf->cl_addr)) {
+                   !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
                        status = nfserr_clid_inuse;
                        goto out;
                }
 
-               slot = &unconf->cl_slot;
-               status = check_slot_seqid(cr_ses->seqid, slot);
+               cs_slot = &unconf->cl_cs_slot;
+               status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status) {
                        /* an unconfirmed replay returns misordered */
                        status = nfserr_seq_misordered;
-                       goto out;
+                       goto out_cache;
                }
 
-               slot->sl_seqid++; /* from 0 to 1 */
+               cs_slot->sl_seqid++; /* from 0 to 1 */
                move_to_confirmed(unconf);
 
                /*
@@ -1407,12 +1425,11 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 
        memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data,
               NFS4_MAX_SESSIONID_LEN);
-       cr_ses->seqid = slot->sl_seqid;
+       cr_ses->seqid = cs_slot->sl_seqid;
 
-       slot->sl_inuse = true;
-       cstate->slot = slot;
-       /* Ensure a page is used for the cache */
-       slot->sl_cache_entry.ce_cachethis = 1;
+out_cache:
+       /* cache solo and embedded create sessions under the state lock */
+       nfsd4_cache_create_session(cr_ses, cs_slot, status);
 out:
        nfs4_unlock_state();
        dprintk("%s returns %d\n", __func__, ntohl(status));
@@ -1480,7 +1497,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        slot = &session->se_slots[seq->slotid];
        dprintk("%s: slotid %d\n", __func__, seq->slotid);
 
-       status = check_slot_seqid(seq->seqid, slot);
+       status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_inuse);
        if (status == nfserr_replay_cache) {
                cstate->slot = slot;
                cstate->session = session;
@@ -1488,7 +1505,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
                 * for nfsd4_svc_encode_compoundres processing */
                status = nfsd4_replay_cache_entry(resp, seq);
                cstate->status = nfserr_replay_cache;
-               goto replay_cache;
+               goto out;
        }
        if (status)
                goto out;
@@ -1497,22 +1514,22 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        slot->sl_inuse = true;
        slot->sl_seqid = seq->seqid;
        slot->sl_cache_entry.ce_cachethis = seq->cachethis;
-       /* Always set the cache entry cachethis for solo sequence */
-       if (nfsd4_is_solo_sequence(resp))
-               slot->sl_cache_entry.ce_cachethis = 1;
 
        cstate->slot = slot;
        cstate->session = session;
 
-replay_cache:
-       /* Renew the clientid on success and on replay.
-        * Hold a session reference until done processing the compound:
+       /* Hold a session reference until done processing the compound:
         * nfsd4_put_session called only if the cstate slot is set.
         */
-       renew_client(session->se_client);
        nfsd4_get_session(session);
 out:
        spin_unlock(&sessionid_lock);
+       /* Renew the clientid on success and on replay */
+       if (cstate->session) {
+               nfs4_lock_state();
+               renew_client(session->se_client);
+               nfs4_unlock_state();
+       }
        dprintk("%s: return %d\n", __func__, ntohl(status));
        return status;
 }
@@ -1521,7 +1538,7 @@ __be32
 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                  struct nfsd4_setclientid *setclid)
 {
-       struct sockaddr_in      *sin = svc_addr_in(rqstp);
+       struct sockaddr         *sa = svc_addr(rqstp);
        struct xdr_netobj       clname = { 
                .len = setclid->se_namelen,
                .data = setclid->se_name,
@@ -1553,8 +1570,11 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                /* RFC 3530 14.2.33 CASE 0: */
                status = nfserr_clid_inuse;
                if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
-                       dprintk("NFSD: setclientid: string in use by client"
-                               " at %pI4\n", &conf->cl_addr);
+                       char addr_str[INET6_ADDRSTRLEN];
+                       rpc_ntop((struct sockaddr *) &conf->cl_addr, addr_str,
+                                sizeof(addr_str));
+                       dprintk("NFSD: setclientid: string in use by client "
+                               "at %s\n", addr_str);
                        goto out;
                }
        }
@@ -1616,7 +1636,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                gen_clid(new);
        }
        copy_verf(new, &clverifier);
-       new->cl_addr = sin->sin_addr.s_addr;
+       rpc_copy_addr((struct sockaddr *) &new->cl_addr, sa);
        new->cl_flavor = rqstp->rq_flavor;
        princ = svc_gss_principal(rqstp);
        if (princ) {
@@ -1628,7 +1648,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        }
        copy_cred(&new->cl_cred, &rqstp->rq_cred);
        gen_confirm(new);
-       gen_callback(new, setclid);
+       gen_callback(new, setclid, rpc_get_scope_id(sa));
        add_to_unconfirmed(new, strhashval);
        setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
        setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
@@ -1650,7 +1670,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                         struct nfsd4_compound_state *cstate,
                         struct nfsd4_setclientid_confirm *setclientid_confirm)
 {
-       struct sockaddr_in *sin = svc_addr_in(rqstp);
+       struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
        nfs4_verifier confirm = setclientid_confirm->sc_confirm; 
        clientid_t * clid = &setclientid_confirm->sc_clientid;
@@ -1669,9 +1689,9 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
        unconf = find_unconfirmed_client(clid);
 
        status = nfserr_clid_inuse;
-       if (conf && conf->cl_addr != sin->sin_addr.s_addr)
+       if (conf && !rpc_cmp_addr((struct sockaddr *) &conf->cl_addr, sa))
                goto out;
-       if (unconf && unconf->cl_addr != sin->sin_addr.s_addr)
+       if (unconf && !rpc_cmp_addr((struct sockaddr *) &unconf->cl_addr, sa))
                goto out;
 
        /*