diff options
Diffstat (limited to 'sys/fs/nfsclient/nfs_clstate.c')
-rw-r--r-- | sys/fs/nfsclient/nfs_clstate.c | 1168 |
1 files changed, 1072 insertions, 96 deletions
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index b54805d..8b5acb9 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -86,14 +86,18 @@ __FBSDID("$FreeBSD$"); */ extern struct nfsstats newnfsstats; extern struct nfsreqhead nfsd_reqq; +extern u_int32_t newnfs_false, newnfs_true; +extern int nfscl_debuglevel; NFSREQSPINLOCK; NFSCLSTATEMUTEX; int nfscl_inited = 0; struct nfsclhead nfsclhead; /* Head of clientid list */ int nfscl_deleghighwater = NFSCLDELEGHIGHWATER; +int nfscl_layouthighwater = NFSCLLAYOUTHIGHWATER; #endif /* !APPLEKEXT */ static int nfscl_delegcnt = 0; +static int nfscl_layoutcnt = 0; static int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *, u_int8_t *, u_int32_t, struct nfscllockowner **, struct nfsclopen **); static void nfscl_clrelease(struct nfsclclient *); @@ -109,9 +113,16 @@ static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **, struct nfscllock **, int); static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *); static u_int32_t nfscl_nextcbident(void); -static mount_t nfscl_getmnt(u_int32_t); +static mount_t nfscl_getmnt(int, uint8_t *, u_int32_t, struct nfsclclient **); +static struct nfsclclient *nfscl_getclnt(u_int32_t); +static struct nfsclclient *nfscl_getclntsess(uint8_t *); static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, int); +static void nfscl_retoncloselayout(struct nfsclclient *, uint8_t *, int); +static void nfscl_reldevinfo_locked(struct nfscldevinfo *); +static struct nfscllayout *nfscl_findlayout(struct nfsclclient *, u_int8_t *, + int); +static struct nfscldevinfo *nfscl_finddevinfo(struct nfsclclient *, uint8_t *); static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *, u_int8_t *, struct nfscllock **); static void nfscl_freealllocks(struct nfscllockownerhead *, int); @@ -145,6 +156,15 @@ static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *, struct nfsmount *, NFSPROC_T *); static void nfscl_emptylockowner(struct nfscllockowner *, struct nfscllockownerfhhead *); +static void nfscl_mergeflayouts(struct nfsclflayouthead *, + struct nfsclflayouthead *); +static int nfscl_layoutrecall(int, struct nfscllayout *, uint32_t, uint64_t, + uint64_t, uint32_t, struct nfsclrecalllayout *); +static int nfscl_seq(uint32_t, uint32_t); +static void nfscl_layoutreturn(struct nfsmount *, struct nfscllayout *, + struct ucred *, NFSPROC_T *); +static void nfscl_dolayoutcommit(struct nfsmount *, struct nfscllayout *, + struct ucred *, NFSPROC_T *); static short nfscberr_null[] = { 0, @@ -214,7 +234,7 @@ nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, if (nfhp != NULL) MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) + fhlen - 1, M_NFSCLOPEN, M_WAITOK); - ret = nfscl_getcl(vp, cred, p, &clp); + ret = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); if (ret != 0) { FREE((caddr_t)nowp, M_NFSCLOWNER); if (nop != NULL) @@ -451,7 +471,7 @@ nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) */ APPLESTATIC int nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, - struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp, + int fords, struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp, void **lckpp) { struct nfsclclient *clp; @@ -466,11 +486,14 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, *lckpp = NULL; /* * Initially, just set the special stateid of all zeros. + * (Don't do this for a DS, since the special stateid can't be used.) */ - stateidp->seqid = 0; - stateidp->other[0] = 0; - stateidp->other[1] = 0; - stateidp->other[2] = 0; + if (fords == 0) { + stateidp->seqid = 0; + stateidp->other[0] = 0; + stateidp->other[1] = 0; + stateidp->other[2] = 0; + } if (vnode_vtype(vp) != VREG) return (EISDIR); np = VTONFS(vp); @@ -526,7 +549,8 @@ nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode, lp = NULL; error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, own, own, mode, &lp, &op); - if (error == 0 && lp != NULL) { + if (error == 0 && lp != NULL && fords == 0) { + /* Don't return a lock stateid for a DS. */ stateidp->seqid = lp->nfsl_stateid.seqid; stateidp->other[0] = @@ -697,21 +721,21 @@ nfscl_openrelease(struct nfsclopen *op, int error, int candelete) * If the "cred" argument is NULL, a new clientid should not be created. * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot * be done. + * The start_renewthread argument tells nfscl_getcl() to start a renew + * thread if this creates a new clp. * It always clpp with a reference count on it, unless returning an error. */ APPLESTATIC int -nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, - struct nfsclclient **clpp) +nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p, + int start_renewthread, struct nfsclclient **clpp) { struct nfsclclient *clp; struct nfsclclient *newclp = NULL; - struct mount *mp; struct nfsmount *nmp; char uuid[HOSTUUIDLEN]; int igotlock = 0, error, trystalecnt, clidinusedelay, i; u_int16_t idlen = 0; - mp = vnode_mount(vp); nmp = VFSTONFS(mp); if (cred != NULL) { getcredhostuuid(cred, uuid, sizeof uuid); @@ -722,7 +746,7 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */ MALLOC(newclp, struct nfsclclient *, sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT, - M_WAITOK); + M_WAITOK | M_ZERO); } NFSLOCKCLSTATE(); /* @@ -743,12 +767,15 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, return (EACCES); } clp = newclp; - NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1); clp->nfsc_idlen = idlen; LIST_INIT(&clp->nfsc_owner); TAILQ_INIT(&clp->nfsc_deleg); + TAILQ_INIT(&clp->nfsc_layout); + LIST_INIT(&clp->nfsc_devinfo); for (i = 0; i < NFSCLDELEGHASHSIZE; i++) LIST_INIT(&clp->nfsc_deleghash[i]); + for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++) + LIST_INIT(&clp->nfsc_layouthash[i]); clp->nfsc_flags = NFSCLFLAGS_INITED; clp->nfsc_clientidrev = 1; clp->nfsc_cbident = nfscl_nextcbident(); @@ -758,11 +785,12 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, nmp->nm_clp = clp; clp->nfsc_nmp = nmp; NFSUNLOCKCLSTATE(); - nfscl_start_renewthread(clp); + if (start_renewthread != 0) + nfscl_start_renewthread(clp); } else { NFSUNLOCKCLSTATE(); if (newclp != NULL) - FREE((caddr_t)newclp, M_NFSCLCLIENT); + free(newclp, M_NFSCLCLIENT); } NFSLOCKCLSTATE(); while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock && @@ -818,14 +846,15 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, clidinusedelay = 120; trystalecnt = 3; do { - error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)), - clp, cred, p); + error = nfsrpc_setclient(nmp, clp, 0, cred, p); if (error == NFSERR_STALECLIENTID || error == NFSERR_STALEDONTRECOVER || + error == NFSERR_BADSESSION || error == NFSERR_CLIDINUSE) { (void) nfs_catnap(PZERO, error, "nfs_setcl"); } } while (((error == NFSERR_STALECLIENTID || + error == NFSERR_BADSESSION || error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) || (error == NFSERR_CLIDINUSE && --clidinusedelay > 0)); if (error) { @@ -942,7 +971,7 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len, if (recovery) clp = rclp; else - error = nfscl_getcl(vp, cred, p, &clp); + error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); } if (error) { FREE((caddr_t)nlp, M_NFSCLLOCKOWNER); @@ -1277,7 +1306,7 @@ nfscl_checkwritelocked(vnode_t vp, struct flock *fl, end = NFS64BITSSET; } - error = nfscl_getcl(vp, cred, p, &clp); + error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp); if (error) return (1); nfscl_filllockowner(id, own, flags); @@ -1825,19 +1854,24 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p) LIST_REMOVE(clp, nfsc_list); nfscl_delegreturnall(clp, p); cred = newnfs_getcred(); - (void) nfsrpc_setclient(nmp, clp, cred, p); + if (NFSHASNFSV4N(nmp)) { + (void)nfsrpc_destroysession(nmp, clp, cred, p); + (void)nfsrpc_destroyclient(nmp, clp, cred, p); + } else + (void)nfsrpc_setclient(nmp, clp, 0, cred, p); nfscl_cleanclient(clp); nmp->nm_clp = NULL; NFSFREECRED(cred); - FREE((caddr_t)clp, M_NFSCLCLIENT); + free(clp, M_NFSCLCLIENT); } else NFSUNLOCKCLSTATE(); } /* * This function is called when a server replies with NFSERR_STALECLIENTID - * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens - * and Locks with reclaim. If these fail, it deletes the corresponding state. + * NFSERR_STALESTATEID or NFSERR_BADSESSION. It traverses the clientid lists, + * doing Opens and Locks with reclaim. If these fail, it deletes the + * corresponding state. */ static void nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) @@ -1854,7 +1888,8 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) struct nfsreq *rep; u_int64_t len; u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode; - int igotlock = 0, error, trycnt, firstlock, s; + int i, igotlock = 0, error, trycnt, firstlock, s; + struct nfscllayout *lyp, *nlyp; /* * First, lock the client structure, so everyone else will @@ -1871,10 +1906,22 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) nmp = clp->nfsc_nmp; if (nmp == NULL) panic("nfscl recover"); + + /* + * For now, just get rid of all layouts. There may be a need + * to do LayoutCommit Ops with reclaim == true later. + */ + TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) + nfscl_freelayout(lyp); + TAILQ_INIT(&clp->nfsc_layout); + for (i = 0; i < NFSCLLAYOUTHASHSIZE; i++) + LIST_INIT(&clp->nfsc_layouthash[i]); + trycnt = 5; do { - error = nfsrpc_setclient(nmp, clp, cred, p); + error = nfsrpc_setclient(nmp, clp, 1, cred, p); } while ((error == NFSERR_STALECLIENTID || + error == NFSERR_BADSESSION || error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); if (error) { nfscl_cleanclient(clp); @@ -1893,9 +1940,10 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) * Mark requests already queued on the server, so that they don't * initiate another recovery cycle. Any requests already in the * queue that handle state information will have the old stale - * clientid/stateid and will get a NFSERR_STALESTATEID or - * NFSERR_STALECLIENTID reply from the server. This will be - * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set. + * clientid/stateid and will get a NFSERR_STALESTATEID, + * NFSERR_STALECLIENTID or NFSERR_BADSESSION reply from the server. + * This will be translated to NFSERR_STALEDONTRECOVER when + * R_DONTRECOVER is set. */ s = splsoftclock(); NFSLOCKREQ(); @@ -2136,6 +2184,10 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p) FREE((caddr_t)dp, M_NFSCLDELEG); } + /* For NFSv4.1 or later, do a RECLAIM_COMPLETE. */ + if (NFSHASNFSV4N(nmp)) + (void)nfsrpc_reclaimcomplete(nmp, cred, p); + NFSLOCKCLSTATE(); clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG; wakeup(&clp->nfsc_flags); @@ -2190,8 +2242,9 @@ nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p) cred = newnfs_getcred(); trycnt = 5; do { - error = nfsrpc_setclient(nmp, clp, cred, p); + error = nfsrpc_setclient(nmp, clp, 0, cred, p); } while ((error == NFSERR_STALECLIENTID || + error == NFSERR_BADSESSION || error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); if (error) { /* @@ -2398,6 +2451,11 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) static time_t prevsec = 0; struct nfscllockownerfh *lfhp, *nlfhp; struct nfscllockownerfhhead lfh; + struct nfscllayout *lyp, *nlyp; + struct nfscldevinfo *dip, *ndip; + struct nfscllayouthead rlh; + struct nfsclrecalllayout *recallp; + struct nfsclds *dsp; cred = newnfs_getcred(); NFSLOCKCLSTATE(); @@ -2425,10 +2483,12 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) { clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew; clidrev = clp->nfsc_clientidrev; - error = nfsrpc_renew(clp, cred, p); + error = nfsrpc_renew(clp, + TAILQ_FIRST(&clp->nfsc_nmp->nm_sess), cred, p); if (error == NFSERR_CBPATHDOWN) cbpathdown = 1; - else if (error == NFSERR_STALECLIENTID) { + else if (error == NFSERR_STALECLIENTID || + error == NFSERR_BADSESSION) { NFSLOCKCLSTATE(); clp->nfsc_flags |= NFSCLFLAGS_RECOVER; NFSUNLOCKCLSTATE(); @@ -2436,6 +2496,25 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p) (void) nfscl_hasexpired(clp, clidrev, p); } + /* Do renews for any DS sessions. */ +checkdsrenew: + NFSLOCKMNT(clp->nfsc_nmp); + /* Skip first entry, since the MDS is handled above. */ + dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess); + if (dsp != NULL) + dsp = TAILQ_NEXT(dsp, nfsclds_list); + while (dsp != NULL) { + if (dsp->nfsclds_expire <= NFSD_MONOSEC) { + dsp->nfsclds_expire = NFSD_MONOSEC + + clp->nfsc_renew; + NFSUNLOCKMNT(clp->nfsc_nmp); + (void)nfsrpc_renew(clp, dsp, cred, p); + goto checkdsrenew; + } + dsp = TAILQ_NEXT(dsp, nfsclds_list); + } + NFSUNLOCKMNT(clp->nfsc_nmp); + TAILQ_INIT(&dh); NFSLOCKCLSTATE(); if (cbpathdown) @@ -2542,8 +2621,90 @@ tryagain: } if (igotlock) nfsv4_unlock(&clp->nfsc_lock, 0); + + /* + * Do the recall on any layouts. To avoid trouble, always + * come back up here after having slept. + */ + TAILQ_INIT(&rlh); +tryagain2: + TAILQ_FOREACH_SAFE(lyp, &clp->nfsc_layout, nfsly_list, nlyp) { + if ((lyp->nfsly_flags & NFSLY_RECALL) != 0) { + /* + * Wait for outstanding I/O ops to be done. + */ + if (lyp->nfsly_lock.nfslock_usecnt > 0 || + (lyp->nfsly_lock.nfslock_lock & + NFSV4LOCK_LOCK) != 0) { + lyp->nfsly_lock.nfslock_lock |= + NFSV4LOCK_WANTED; + (void)nfsmsleep(&lyp->nfsly_lock, + NFSCLSTATEMUTEXPTR, PZERO, "nfslyp", + NULL); + goto tryagain2; + } + /* Move the layout to the recall list. */ + TAILQ_REMOVE(&clp->nfsc_layout, lyp, + nfsly_list); + LIST_REMOVE(lyp, nfsly_hash); + TAILQ_INSERT_HEAD(&rlh, lyp, nfsly_list); + + /* Handle any layout commits. */ + if (!NFSHASNOLAYOUTCOMMIT(clp->nfsc_nmp) && + (lyp->nfsly_flags & NFSLY_WRITTEN) != 0) { + lyp->nfsly_flags &= ~NFSLY_WRITTEN; + NFSUNLOCKCLSTATE(); + NFSCL_DEBUG(3, "do layoutcommit\n"); + nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, + cred, p); + NFSLOCKCLSTATE(); + goto tryagain2; + } + } + } + + /* Now, look for stale layouts. */ + lyp = TAILQ_LAST(&clp->nfsc_layout, nfscllayouthead); + while (lyp != NULL) { + nlyp = TAILQ_PREV(lyp, nfscllayouthead, nfsly_list); + if (lyp->nfsly_timestamp < NFSD_MONOSEC && + (lyp->nfsly_flags & NFSLY_RECALL) == 0 && + lyp->nfsly_lock.nfslock_usecnt == 0 && + lyp->nfsly_lock.nfslock_lock == 0) { + NFSCL_DEBUG(4, "ret stale lay=%d\n", + nfscl_layoutcnt); + recallp = malloc(sizeof(*recallp), + M_NFSLAYRECALL, M_NOWAIT); + if (recallp == NULL) + break; + (void)nfscl_layoutrecall(NFSLAYOUTRETURN_FILE, + lyp, NFSLAYOUTIOMODE_ANY, 0, UINT64_MAX, + lyp->nfsly_stateid.seqid, recallp); + } + lyp = nlyp; + } + + /* + * Free up any unreferenced device info structures. + */ + LIST_FOREACH_SAFE(dip, &clp->nfsc_devinfo, nfsdi_list, ndip) { + if (dip->nfsdi_layoutrefs == 0 && + dip->nfsdi_refcnt == 0) { + NFSCL_DEBUG(4, "freeing devinfo\n"); + LIST_REMOVE(dip, nfsdi_list); + nfscl_freedevinfo(dip); + } + } NFSUNLOCKCLSTATE(); + /* Do layout return(s), as required. */ + TAILQ_FOREACH_SAFE(lyp, &rlh, nfsly_list, nlyp) { + TAILQ_REMOVE(&rlh, lyp, nfsly_list); + NFSCL_DEBUG(4, "ret layout\n"); + nfscl_layoutreturn(clp->nfsc_nmp, lyp, cred, p); + nfscl_freelayout(lyp); + } + /* * Delegreturn any delegations cleaned out or recalled. */ @@ -2599,8 +2760,8 @@ tryagain: } /* - * Initiate state recovery. Called when NFSERR_STALECLIENTID or - * NFSERR_STALESTATEID is received. + * Initiate state recovery. Called when NFSERR_STALECLIENTID, + * NFSERR_STALESTATEID or NFSERR_BADSESSION is received. */ APPLESTATIC void nfscl_initiate_recovery(struct nfsclclient *clp) @@ -2832,7 +2993,7 @@ nfscl_getclose(vnode_t vp, struct nfsclclient **clpp) struct nfsfh *nfhp; int error, notdecr; - error = nfscl_getcl(vp, NULL, NULL, &clp); + error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp); if (error) return (error); *clpp = clp; @@ -2906,7 +3067,7 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) struct nfsfh *nfhp; int error; - error = nfscl_getcl(vp, NULL, NULL, &clp); + error = nfscl_getcl(vnode_mount(vp), NULL, NULL, 1, &clp); if (error) return (error); *clpp = clp; @@ -2930,6 +3091,9 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) } } + /* Return any layouts marked return on close. */ + nfscl_retoncloselayout(clp, nfhp->nfh_fh, nfhp->nfh_len); + /* Now process the opens against the server. */ lookformore: LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { @@ -2979,11 +3143,11 @@ nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p) APPLESTATIC void nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) { - int i, op; + int clist, gotseq_ok, i, j, k, op, rcalls; u_int32_t *tl; struct nfsclclient *clp; struct nfscldeleg *dp = NULL; - int numops, taglen = -1, error = 0, trunc, ret = 0; + int numops, taglen = -1, error = 0, trunc; u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident; u_char tag[NFSV4_SMALLSTR + 1], *tagstr; vnode_t vp = NULL; @@ -2993,7 +3157,16 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) mount_t mp; nfsattrbit_t attrbits, rattrbits; nfsv4stateid_t stateid; - + uint32_t seqid, slotid = 0, highslot, cachethis; + uint8_t sessionid[NFSX_V4SESSIONID]; + struct mbuf *rep; + struct nfscllayout *lyp; + uint64_t filesid[2], len, off; + int changed, gotone, laytype, recalltype; + uint32_t iomode; + struct nfsclrecalllayout *recallp = NULL; + + gotseq_ok = 0; nfsrvd_rephead(nd); NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); taglen = fxdr_unsigned(int, *tl); @@ -3019,7 +3192,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED); NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); minorvers = fxdr_unsigned(u_int32_t, *tl++); - if (minorvers != NFSV4_MINORVERSION) + if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION) nd->nd_repstat = NFSERR_MINORVERMISMATCH; cbident = fxdr_unsigned(u_int32_t, *tl++); if (nd->nd_repstat) @@ -3034,73 +3207,85 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED); *repp++ = *tl; op = fxdr_unsigned(int, *tl); - if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) { + if (op < NFSV4OP_CBGETATTR || + (op > NFSV4OP_CBRECALL && minorvers == NFSV4_MINORVERSION) || + (op > NFSV4OP_CBNOTIFYDEVID && + minorvers == NFSV41_MINORVERSION)) { nd->nd_repstat = NFSERR_OPILLEGAL; *repp = nfscl_errmap(nd); retops++; break; } nd->nd_procnum = op; - newnfsstats.cbrpccnt[nd->nd_procnum]++; + if (op < NFSV4OP_CBNOPS) + newnfsstats.cbrpccnt[nd->nd_procnum]++; switch (op) { case NFSV4OP_CBGETATTR: - clp = NULL; + NFSCL_DEBUG(4, "cbgetattr\n"); + mp = NULL; + vp = NULL; error = nfsm_getfh(nd, &nfhp); if (!error) error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL); + if (error == 0 && i == 0 && + minorvers != NFSV4_MINORVERSION) + error = NFSERR_OPNOTINSESS; if (!error) { - mp = nfscl_getmnt(cbident); + mp = nfscl_getmnt(minorvers, sessionid, cbident, + &clp); if (mp == NULL) error = NFSERR_SERVERFAULT; } if (!error) { - dp = NULL; - NFSLOCKCLSTATE(); - clp = nfscl_findcl(VFSTONFS(mp)); - if (clp != NULL) - dp = nfscl_finddeleg(clp, nfhp->nfh_fh, - nfhp->nfh_len); - NFSUNLOCKCLSTATE(); - if (dp == NULL) - error = NFSERR_SERVERFAULT; - } - if (!error) { - ret = nfscl_ngetreopen(mp, nfhp->nfh_fh, + error = nfscl_ngetreopen(mp, nfhp->nfh_fh, nfhp->nfh_len, p, &np); - if (!ret) + if (!error) vp = NFSTOV(np); } - if (nfhp != NULL) - FREE((caddr_t)nfhp, M_NFSFH); if (!error) { NFSZERO_ATTRBIT(&rattrbits); - if (NFSISSET_ATTRBIT(&attrbits, - NFSATTRBIT_SIZE)) { - if (!ret) - va.va_size = np->n_size; - else - va.va_size = dp->nfsdl_size; - NFSSETBIT_ATTRBIT(&rattrbits, - NFSATTRBIT_SIZE); - } - if (NFSISSET_ATTRBIT(&attrbits, - NFSATTRBIT_CHANGE)) { - va.va_filerev = dp->nfsdl_change; - if (ret || (np->n_flag & NDELEGMOD)) - va.va_filerev++; - NFSSETBIT_ATTRBIT(&rattrbits, - NFSATTRBIT_CHANGE); - } + NFSLOCKCLSTATE(); + dp = nfscl_finddeleg(clp, nfhp->nfh_fh, + nfhp->nfh_len); + if (dp != NULL) { + if (NFSISSET_ATTRBIT(&attrbits, + NFSATTRBIT_SIZE)) { + if (vp != NULL) + va.va_size = np->n_size; + else + va.va_size = + dp->nfsdl_size; + NFSSETBIT_ATTRBIT(&rattrbits, + NFSATTRBIT_SIZE); + } + if (NFSISSET_ATTRBIT(&attrbits, + NFSATTRBIT_CHANGE)) { + va.va_filerev = + dp->nfsdl_change; + if (vp == NULL || + (np->n_flag & NDELEGMOD)) + va.va_filerev++; + NFSSETBIT_ATTRBIT(&rattrbits, + NFSATTRBIT_CHANGE); + } + } else + error = NFSERR_SERVERFAULT; + NFSUNLOCKCLSTATE(); + } + if (vp != NULL) + vrele(vp); + if (mp != NULL) + vfs_unbusy(mp); + if (nfhp != NULL) + FREE((caddr_t)nfhp, M_NFSFH); + if (!error) (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va, NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0); - if (!ret) - vrele(vp); - } break; case NFSV4OP_CBRECALL: - clp = NULL; + NFSCL_DEBUG(4, "cbrecall\n"); NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED); stateid.seqid = *tl++; @@ -3109,14 +3294,15 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); trunc = fxdr_unsigned(int, *tl); error = nfsm_getfh(nd, &nfhp); - if (!error) { - mp = nfscl_getmnt(cbident); - if (mp == NULL) - error = NFSERR_SERVERFAULT; - } + if (error == 0 && i == 0 && + minorvers != NFSV4_MINORVERSION) + error = NFSERR_OPNOTINSESS; if (!error) { NFSLOCKCLSTATE(); - clp = nfscl_findcl(VFSTONFS(mp)); + if (minorvers == NFSV4_MINORVERSION) + clp = nfscl_getclnt(cbident); + else + clp = nfscl_getclntsess(sessionid); if (clp != NULL) { dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); @@ -3134,6 +3320,195 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) if (nfhp != NULL) FREE((caddr_t)nfhp, M_NFSFH); break; + case NFSV4OP_CBLAYOUTRECALL: + NFSCL_DEBUG(4, "cblayrec\n"); + nfhp = NULL; + NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED); + laytype = fxdr_unsigned(int, *tl++); + iomode = fxdr_unsigned(uint32_t, *tl++); + if (newnfs_true == *tl++) + changed = 1; + else + changed = 0; + recalltype = fxdr_unsigned(int, *tl); + recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, + M_WAITOK); + if (laytype != NFSLAYOUT_NFSV4_1_FILES) + error = NFSERR_NOMATCHLAYOUT; + else if (recalltype == NFSLAYOUTRETURN_FILE) { + error = nfsm_getfh(nd, &nfhp); + NFSCL_DEBUG(4, "retfile getfh=%d\n", error); + if (error != 0) + goto nfsmout; + NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER + + NFSX_STATEID); + off = fxdr_hyper(tl); tl += 2; + len = fxdr_hyper(tl); tl += 2; + stateid.seqid = fxdr_unsigned(uint32_t, *tl++); + NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER); + if (minorvers == NFSV4_MINORVERSION) + error = NFSERR_NOTSUPP; + else if (i == 0) + error = NFSERR_OPNOTINSESS; + if (error == 0) { + NFSLOCKCLSTATE(); + clp = nfscl_getclntsess(sessionid); + NFSCL_DEBUG(4, "cbly clp=%p\n", clp); + if (clp != NULL) { + lyp = nfscl_findlayout(clp, + nfhp->nfh_fh, + nfhp->nfh_len); + NFSCL_DEBUG(4, "cblyp=%p\n", + lyp); + if (lyp != NULL && + (lyp->nfsly_flags & + NFSLY_FILES) != 0 && + !NFSBCMP(stateid.other, + lyp->nfsly_stateid.other, + NFSX_STATEIDOTHER)) { + error = + nfscl_layoutrecall( + recalltype, + lyp, iomode, off, + len, stateid.seqid, + recallp); + recallp = NULL; + wakeup(clp); + NFSCL_DEBUG(4, + "aft layrcal=%d\n", + error); + } else + error = + NFSERR_NOMATCHLAYOUT; + } else + error = NFSERR_NOMATCHLAYOUT; + NFSUNLOCKCLSTATE(); + } + free(nfhp, M_NFSFH); + } else if (recalltype == NFSLAYOUTRETURN_FSID) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER); + filesid[0] = fxdr_hyper(tl); tl += 2; + filesid[1] = fxdr_hyper(tl); tl += 2; + gotone = 0; + NFSLOCKCLSTATE(); + clp = nfscl_getclntsess(sessionid); + if (clp != NULL) { + TAILQ_FOREACH(lyp, &clp->nfsc_layout, + nfsly_list) { + if (lyp->nfsly_filesid[0] == + filesid[0] && + lyp->nfsly_filesid[1] == + filesid[1]) { + error = + nfscl_layoutrecall( + recalltype, + lyp, iomode, 0, + UINT64_MAX, + lyp->nfsly_stateid.seqid, + recallp); + recallp = NULL; + gotone = 1; + } + } + if (gotone != 0) + wakeup(clp); + else + error = NFSERR_NOMATCHLAYOUT; + } else + error = NFSERR_NOMATCHLAYOUT; + NFSUNLOCKCLSTATE(); + } else if (recalltype == NFSLAYOUTRETURN_ALL) { + gotone = 0; + NFSLOCKCLSTATE(); + clp = nfscl_getclntsess(sessionid); + if (clp != NULL) { + TAILQ_FOREACH(lyp, &clp->nfsc_layout, + nfsly_list) { + error = nfscl_layoutrecall( + recalltype, lyp, iomode, 0, + UINT64_MAX, + lyp->nfsly_stateid.seqid, + recallp); + recallp = NULL; + gotone = 1; + } + if (gotone != 0) + wakeup(clp); + else + error = NFSERR_NOMATCHLAYOUT; + } else + error = NFSERR_NOMATCHLAYOUT; + NFSUNLOCKCLSTATE(); + } else + error = NFSERR_NOMATCHLAYOUT; + if (recallp != NULL) { + free(recallp, M_NFSLAYRECALL); + recallp = NULL; + } + break; + case NFSV4OP_CBSEQUENCE: + NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + + 5 * NFSX_UNSIGNED); + bcopy(tl, sessionid, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + seqid = fxdr_unsigned(uint32_t, *tl++); + slotid = fxdr_unsigned(uint32_t, *tl++); + highslot = fxdr_unsigned(uint32_t, *tl++); + cachethis = *tl++; + /* Throw away the referring call stuff. */ + clist = fxdr_unsigned(int, *tl); + for (j = 0; j < clist; j++) { + NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + + NFSX_UNSIGNED); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + rcalls = fxdr_unsigned(int, *tl); + for (k = 0; k < rcalls; k++) { + NFSM_DISSECT(tl, uint32_t *, + 2 * NFSX_UNSIGNED); + } + } + NFSLOCKCLSTATE(); + if (i == 0) { + clp = nfscl_getclntsess(sessionid); + if (clp == NULL) + error = NFSERR_SERVERFAULT; + } else + error = NFSERR_SEQUENCEPOS; + if (error == 0) + error = nfsv4_seqsession(seqid, slotid, + highslot, + NFSMNT_MDSSESSION(clp->nfsc_nmp)-> + nfsess_cbslots, &rep, + NFSMNT_MDSSESSION(clp->nfsc_nmp)-> + nfsess_backslots); + NFSUNLOCKCLSTATE(); + if (error == 0) { + gotseq_ok = 1; + if (rep != NULL) { + NFSCL_DEBUG(4, "Got cbretry\n"); + m_freem(nd->nd_mreq); + nd->nd_mreq = rep; + rep = NULL; + goto out; + } + NFSM_BUILD(tl, uint32_t *, + NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED); + bcopy(sessionid, tl, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + *tl++ = txdr_unsigned(seqid); + *tl++ = txdr_unsigned(slotid); + *tl++ = txdr_unsigned(NFSV4_CBSLOTS - 1); + *tl = txdr_unsigned(NFSV4_CBSLOTS - 1); + } + break; + default: + if (i == 0 && minorvers == NFSV41_MINORVERSION) + error = NFSERR_OPNOTINSESS; + else { + NFSCL_DEBUG(1, "unsupp callback %d\n", op); + error = NFSERR_NOTSUPP; + } + break; }; if (error) { if (error == EBADRPC || error == NFSERR_BADXDR) { @@ -3151,6 +3526,8 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) *repp = 0; /* NFS4_OK */ } nfsmout: + if (recallp != NULL) + free(recallp, M_NFSLAYRECALL); if (error) { if (error == EBADRPC || error == NFSERR_BADXDR) nd->nd_repstat = NFSERR_BADXDR; @@ -3165,6 +3542,21 @@ nfsmout: *retopsp = txdr_unsigned(retops); } *nd->nd_errp = nfscl_errmap(nd); +out: + if (gotseq_ok != 0) { + rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); + NFSLOCKCLSTATE(); + clp = nfscl_getclntsess(sessionid); + if (clp != NULL) { + nfsv4_seqsess_cacherep(slotid, + NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots, + rep); + NFSUNLOCKCLSTATE(); + } else { + NFSUNLOCKCLSTATE(); + m_freem(rep); + } + } } /* @@ -3204,26 +3596,68 @@ nfscl_nextcbident(void) } /* - * Get the mount point related to a given cbident. + * Get the mount point related to a given cbident or session and busy it. */ static mount_t -nfscl_getmnt(u_int32_t cbident) +nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident, + struct nfsclclient **clpp) { struct nfsclclient *clp; - struct nfsmount *nmp; + mount_t mp; + int error; + *clpp = NULL; NFSLOCKCLSTATE(); LIST_FOREACH(clp, &nfsclhead, nfsc_list) { - if (clp->nfsc_cbident == cbident) + if (minorvers == NFSV4_MINORVERSION) { + if (clp->nfsc_cbident == cbident) + break; + } else if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)-> + nfsess_sessionid, sessionid, NFSX_V4SESSIONID)) break; } if (clp == NULL) { NFSUNLOCKCLSTATE(); return (NULL); } - nmp = clp->nfsc_nmp; + mp = clp->nfsc_nmp->nm_mountp; + vfs_ref(mp); NFSUNLOCKCLSTATE(); - return (nmp->nm_mountp); + error = vfs_busy(mp, 0); + vfs_rel(mp); + if (error != 0) + return (NULL); + *clpp = clp; + return (mp); +} + +/* + * Get the clientid pointer related to a given cbident. + */ +static struct nfsclclient * +nfscl_getclnt(u_int32_t cbident) +{ + struct nfsclclient *clp; + + LIST_FOREACH(clp, &nfsclhead, nfsc_list) + if (clp->nfsc_cbident == cbident) + break; + return (clp); +} + +/* + * Get the clientid pointer related to a given sessionid. + */ +static struct nfsclclient * +nfscl_getclntsess(uint8_t *sessionid) +{ + struct nfsclclient *clp; + + LIST_FOREACH(clp, &nfsclhead, nfsc_list) + if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_sessionid, + sessionid, NFSX_V4SESSIONID)) + break; + return (clp); } /* @@ -3420,7 +3854,8 @@ nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, ret = nfscl_moveopen(vp, clp, nmp, lop, owp, dp, cred, p); if (ret == NFSERR_STALECLIENTID || - ret == NFSERR_STALEDONTRECOVER) { + ret == NFSERR_STALEDONTRECOVER || + ret == NFSERR_BADSESSION) { if (gotvp) vrele(vp); return (ret); @@ -3451,7 +3886,8 @@ nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, if (ret) { nfscl_freeopenowner(owp, 0); if (ret == NFSERR_STALECLIENTID || - ret == NFSERR_STALEDONTRECOVER) { + ret == NFSERR_STALEDONTRECOVER || + ret == NFSERR_BADSESSION) { if (gotvp) vrele(vp); return (ret); @@ -3475,7 +3911,8 @@ nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p); if (ret == NFSERR_STALESTATEID || ret == NFSERR_STALEDONTRECOVER || - ret == NFSERR_STALECLIENTID) { + ret == NFSERR_STALECLIENTID || + ret == NFSERR_BADSESSION) { if (gotvp) vrele(vp); return (ret); @@ -4223,10 +4660,549 @@ nfscl_errmap(struct nfsrv_descript *nd) if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || nd->nd_repstat == NFSERR_OPILLEGAL) return (txdr_unsigned(nd->nd_repstat)); - errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; + if (nd->nd_procnum < NFSV4OP_CBNOPS) + errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; + else + return (txdr_unsigned(nd->nd_repstat)); while (*++errp) if (*errp == (short)nd->nd_repstat) return (txdr_unsigned(nd->nd_repstat)); return (txdr_unsigned(*defaulterrp)); } +/* + * Called to find/add a layout to a client. + * This function returns the layout with a refcnt (shared lock) upon + * success (returns 0) or with no lock/refcnt on the layout when an + * error is returned. + * If a layout is passed in via lypp, it is locked (exclusively locked). + */ +APPLESTATIC int +nfscl_layout(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, + nfsv4stateid_t *stateidp, int retonclose, + struct nfsclflayouthead *fhlp, struct nfscllayout **lypp, + struct ucred *cred, NFSPROC_T *p) +{ + struct nfsclclient *clp; + struct nfscllayout *lyp, *tlyp; + struct nfsclflayout *flp; + struct nfsnode *np = VTONFS(vp); + mount_t mp; + int layout_passed_in; + + mp = nmp->nm_mountp; + layout_passed_in = 1; + tlyp = NULL; + lyp = *lypp; + if (lyp == NULL) { + layout_passed_in = 0; + tlyp = malloc(sizeof(*tlyp) + fhlen - 1, M_NFSLAYOUT, + M_WAITOK | M_ZERO); + } + + NFSLOCKCLSTATE(); + clp = nmp->nm_clp; + if (clp == NULL) { + if (layout_passed_in != 0) + nfsv4_unlock(&lyp->nfsly_lock, 0); + NFSUNLOCKCLSTATE(); + if (tlyp != NULL) + free(tlyp, M_NFSLAYOUT); + return (EPERM); + } + if (lyp == NULL) { + /* + * Although no lyp was passed in, another thread might have + * allocated one. If one is found, just increment it's ref + * count and return it. + */ + lyp = nfscl_findlayout(clp, fhp, fhlen); + if (lyp == NULL) { + lyp = tlyp; + tlyp = NULL; + lyp->nfsly_stateid.seqid = stateidp->seqid; + lyp->nfsly_stateid.other[0] = stateidp->other[0]; + lyp->nfsly_stateid.other[1] = stateidp->other[1]; + lyp->nfsly_stateid.other[2] = stateidp->other[2]; + lyp->nfsly_lastbyte = 0; + LIST_INIT(&lyp->nfsly_flayread); + LIST_INIT(&lyp->nfsly_flayrw); + LIST_INIT(&lyp->nfsly_recall); + lyp->nfsly_filesid[0] = np->n_vattr.na_filesid[0]; + lyp->nfsly_filesid[1] = np->n_vattr.na_filesid[1]; + lyp->nfsly_clp = clp; + lyp->nfsly_flags = (retonclose != 0) ? + (NFSLY_FILES | NFSLY_RETONCLOSE) : NFSLY_FILES; + lyp->nfsly_fhlen = fhlen; + NFSBCOPY(fhp, lyp->nfsly_fh, fhlen); + TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); + LIST_INSERT_HEAD(NFSCLLAYOUTHASH(clp, fhp, fhlen), lyp, + nfsly_hash); + lyp->nfsly_timestamp = NFSD_MONOSEC + 120; + nfscl_layoutcnt++; + } else { + if (retonclose != 0) + lyp->nfsly_flags |= NFSLY_RETONCLOSE; + TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list); + TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); + lyp->nfsly_timestamp = NFSD_MONOSEC + 120; + } + nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp); + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + NFSUNLOCKCLSTATE(); + if (tlyp != NULL) + free(tlyp, M_NFSLAYOUT); + return (EPERM); + } + *lypp = lyp; + } else + lyp->nfsly_stateid.seqid = stateidp->seqid; + + /* Merge the new list of File Layouts into the list. */ + flp = LIST_FIRST(fhlp); + if (flp != NULL) { + if (flp->nfsfl_iomode == NFSLAYOUTIOMODE_READ) + nfscl_mergeflayouts(&lyp->nfsly_flayread, fhlp); + else + nfscl_mergeflayouts(&lyp->nfsly_flayrw, fhlp); + } + if (layout_passed_in != 0) + nfsv4_unlock(&lyp->nfsly_lock, 1); + NFSUNLOCKCLSTATE(); + if (tlyp != NULL) + free(tlyp, M_NFSLAYOUT); + return (0); +} + +/* + * Search for a layout by MDS file handle. + * If one is found, it is returned with a refcnt (shared lock) iff + * retflpp returned non-NULL and locked (exclusive locked) iff retflpp is + * returned NULL. + */ +struct nfscllayout * +nfscl_getlayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen, + uint64_t off, struct nfsclflayout **retflpp, int *recalledp) +{ + struct nfscllayout *lyp; + mount_t mp; + int error, igotlock; + + mp = clp->nfsc_nmp->nm_mountp; + *recalledp = 0; + *retflpp = NULL; + NFSLOCKCLSTATE(); + lyp = nfscl_findlayout(clp, fhp, fhlen); + if (lyp != NULL) { + if ((lyp->nfsly_flags & NFSLY_RECALL) == 0) { + TAILQ_REMOVE(&clp->nfsc_layout, lyp, nfsly_list); + TAILQ_INSERT_HEAD(&clp->nfsc_layout, lyp, nfsly_list); + lyp->nfsly_timestamp = NFSD_MONOSEC + 120; + error = nfscl_findlayoutforio(lyp, off, + NFSV4OPEN_ACCESSREAD, retflpp); + if (error == 0) + nfsv4_getref(&lyp->nfsly_lock, NULL, + NFSCLSTATEMUTEXPTR, mp); + else { + do { + igotlock = nfsv4_lock(&lyp->nfsly_lock, + 1, NULL, NFSCLSTATEMUTEXPTR, mp); + } while (igotlock == 0 && + (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0); + *retflpp = NULL; + } + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + lyp = NULL; + *recalledp = 1; + } + } else { + lyp = NULL; + *recalledp = 1; + } + } + NFSUNLOCKCLSTATE(); + return (lyp); +} + +/* + * Search for a layout by MDS file handle. If one is found that is marked + * "return on close", delete it, since it should now be forgotten. + */ +static void +nfscl_retoncloselayout(struct nfsclclient *clp, uint8_t *fhp, int fhlen) +{ + struct nfscllayout *lyp; + +tryagain: + lyp = nfscl_findlayout(clp, fhp, fhlen); + if (lyp != NULL && (lyp->nfsly_flags & NFSLY_RETONCLOSE) != 0) { + /* + * Wait for outstanding I/O ops to be done. + */ + if (lyp->nfsly_lock.nfslock_usecnt != 0 || + lyp->nfsly_lock.nfslock_lock != 0) { + lyp->nfsly_lock.nfslock_lock |= NFSV4LOCK_WANTED; + (void)mtx_sleep(&lyp->nfsly_lock, + NFSCLSTATEMUTEXPTR, PZERO, "nfslyc", 0); + goto tryagain; + } + nfscl_freelayout(lyp); + } +} + +/* + * Dereference a layout. + */ +void +nfscl_rellayout(struct nfscllayout *lyp, int exclocked) +{ + + NFSLOCKCLSTATE(); + if (exclocked != 0) + nfsv4_unlock(&lyp->nfsly_lock, 0); + else + nfsv4_relref(&lyp->nfsly_lock); + NFSUNLOCKCLSTATE(); +} + +/* + * Search for a devinfo by deviceid. If one is found, return it after + * acquiring a reference count on it. + */ +struct nfscldevinfo * +nfscl_getdevinfo(struct nfsclclient *clp, uint8_t *deviceid, + struct nfscldevinfo *dip) +{ + + NFSLOCKCLSTATE(); + if (dip == NULL) + dip = nfscl_finddevinfo(clp, deviceid); + if (dip != NULL) + dip->nfsdi_refcnt++; + NFSUNLOCKCLSTATE(); + return (dip); +} + +/* + * Dereference a devinfo structure. + */ +static void +nfscl_reldevinfo_locked(struct nfscldevinfo *dip) +{ + + dip->nfsdi_refcnt--; + if (dip->nfsdi_refcnt == 0) + wakeup(&dip->nfsdi_refcnt); +} + +/* + * Dereference a devinfo structure. + */ +void +nfscl_reldevinfo(struct nfscldevinfo *dip) +{ + + NFSLOCKCLSTATE(); + nfscl_reldevinfo_locked(dip); + NFSUNLOCKCLSTATE(); +} + +/* + * Find a layout for this file handle. Return NULL upon failure. + */ +static struct nfscllayout * +nfscl_findlayout(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) +{ + struct nfscllayout *lyp; + + LIST_FOREACH(lyp, NFSCLLAYOUTHASH(clp, fhp, fhlen), nfsly_hash) + if (lyp->nfsly_fhlen == fhlen && + !NFSBCMP(lyp->nfsly_fh, fhp, fhlen)) + break; + return (lyp); +} + +/* + * Find a devinfo for this deviceid. Return NULL upon failure. + */ +static struct nfscldevinfo * +nfscl_finddevinfo(struct nfsclclient *clp, uint8_t *deviceid) +{ + struct nfscldevinfo *dip; + + LIST_FOREACH(dip, &clp->nfsc_devinfo, nfsdi_list) + if (NFSBCMP(dip->nfsdi_deviceid, deviceid, NFSX_V4DEVICEID) + == 0) + break; + return (dip); +} + +/* + * Merge the new file layout list into the main one, maintaining it in + * increasing offset order. + */ +static void +nfscl_mergeflayouts(struct nfsclflayouthead *fhlp, + struct nfsclflayouthead *newfhlp) +{ + struct nfsclflayout *flp, *nflp, *prevflp, *tflp; + + flp = LIST_FIRST(fhlp); + prevflp = NULL; + LIST_FOREACH_SAFE(nflp, newfhlp, nfsfl_list, tflp) { + while (flp != NULL && flp->nfsfl_off < nflp->nfsfl_off) { + prevflp = flp; + flp = LIST_NEXT(flp, nfsfl_list); + } + if (prevflp == NULL) + LIST_INSERT_HEAD(fhlp, nflp, nfsfl_list); + else + LIST_INSERT_AFTER(prevflp, nflp, nfsfl_list); + prevflp = nflp; + } +} + +/* + * Add this nfscldevinfo to the client, if it doesn't already exist. + * This function consumes the structure pointed at by dip, if not NULL. + */ +APPLESTATIC int +nfscl_adddevinfo(struct nfsmount *nmp, struct nfscldevinfo *dip, + struct nfsclflayout *flp) +{ + struct nfsclclient *clp; + struct nfscldevinfo *tdip; + + NFSLOCKCLSTATE(); + clp = nmp->nm_clp; + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + if (dip != NULL) + free(dip, M_NFSDEVINFO); + return (ENODEV); + } + tdip = nfscl_finddevinfo(clp, flp->nfsfl_dev); + if (tdip != NULL) { + tdip->nfsdi_layoutrefs++; + flp->nfsfl_devp = tdip; + nfscl_reldevinfo_locked(tdip); + NFSUNLOCKCLSTATE(); + if (dip != NULL) + free(dip, M_NFSDEVINFO); + return (0); + } + if (dip != NULL) { + LIST_INSERT_HEAD(&clp->nfsc_devinfo, dip, nfsdi_list); + dip->nfsdi_layoutrefs = 1; + flp->nfsfl_devp = dip; + } + NFSUNLOCKCLSTATE(); + if (dip == NULL) + return (ENODEV); + return (0); +} + +/* + * Free up a layout structure and associated file layout structure(s). + */ +APPLESTATIC void +nfscl_freelayout(struct nfscllayout *layp) +{ + struct nfsclflayout *flp, *nflp; + struct nfsclrecalllayout *rp, *nrp; + + LIST_FOREACH_SAFE(flp, &layp->nfsly_flayread, nfsfl_list, nflp) { + LIST_REMOVE(flp, nfsfl_list); + nfscl_freeflayout(flp); + } + LIST_FOREACH_SAFE(flp, &layp->nfsly_flayrw, nfsfl_list, nflp) { + LIST_REMOVE(flp, nfsfl_list); + nfscl_freeflayout(flp); + } + LIST_FOREACH_SAFE(rp, &layp->nfsly_recall, nfsrecly_list, nrp) { + LIST_REMOVE(rp, nfsrecly_list); + free(rp, M_NFSLAYRECALL); + } + nfscl_layoutcnt--; + free(layp, M_NFSLAYOUT); +} + +/* + * Free up a file layout structure. + */ +APPLESTATIC void +nfscl_freeflayout(struct nfsclflayout *flp) +{ + int i; + + for (i = 0; i < flp->nfsfl_fhcnt; i++) + free(flp->nfsfl_fh[i], M_NFSFH); + if (flp->nfsfl_devp != NULL) + flp->nfsfl_devp->nfsdi_layoutrefs--; + free(flp, M_NFSFLAYOUT); +} + +/* + * Free up a file layout devinfo structure. + */ +APPLESTATIC void +nfscl_freedevinfo(struct nfscldevinfo *dip) +{ + + free(dip, M_NFSDEVINFO); +} + +/* + * Mark any layouts that match as recalled. + */ +static int +nfscl_layoutrecall(int recalltype, struct nfscllayout *lyp, uint32_t iomode, + uint64_t off, uint64_t len, uint32_t stateseqid, + struct nfsclrecalllayout *recallp) +{ + struct nfsclrecalllayout *rp, *orp; + + recallp->nfsrecly_recalltype = recalltype; + recallp->nfsrecly_iomode = iomode; + recallp->nfsrecly_stateseqid = stateseqid; + recallp->nfsrecly_off = off; + recallp->nfsrecly_len = len; + /* + * Order the list as file returns first, followed by fsid and any + * returns, both in increasing stateseqid order. + * Note that the seqids wrap around, so 1 is after 0xffffffff. + * (I'm not sure this is correct because I find RFC5661 confusing + * on this, but hopefully it will work ok.) + */ + orp = NULL; + LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) { + orp = rp; + if ((recalltype == NFSLAYOUTRETURN_FILE && + (rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE || + nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) || + (recalltype != NFSLAYOUTRETURN_FILE && + rp->nfsrecly_recalltype != NFSLAYOUTRETURN_FILE && + nfscl_seq(stateseqid, rp->nfsrecly_stateseqid) != 0)) { + LIST_INSERT_BEFORE(rp, recallp, nfsrecly_list); + break; + } + } + if (rp == NULL) { + if (orp == NULL) + LIST_INSERT_HEAD(&lyp->nfsly_recall, recallp, + nfsrecly_list); + else + LIST_INSERT_AFTER(orp, recallp, nfsrecly_list); + } + lyp->nfsly_flags |= NFSLY_RECALL; + return (0); +} + +/* + * Compare the two seqids for ordering. The trick is that the seqids can + * wrap around from 0xffffffff->0, so check for the cases where one + * has wrapped around. + * Return 1 if seqid1 comes before seqid2, 0 otherwise. + */ +static int +nfscl_seq(uint32_t seqid1, uint32_t seqid2) +{ + + if (seqid2 > seqid1 && (seqid2 - seqid1) >= 0x7fffffff) + /* seqid2 has wrapped around. */ + return (0); + if (seqid1 > seqid2 && (seqid1 - seqid2) >= 0x7fffffff) + /* seqid1 has wrapped around. */ + return (1); + if (seqid1 <= seqid2) + return (1); + return (0); +} + +/* + * Do a layout return for each of the recalls. + */ +static void +nfscl_layoutreturn(struct nfsmount *nmp, struct nfscllayout *lyp, + struct ucred *cred, NFSPROC_T *p) +{ + struct nfsclrecalllayout *rp; + nfsv4stateid_t stateid; + + NFSBCOPY(lyp->nfsly_stateid.other, stateid.other, NFSX_STATEIDOTHER); + LIST_FOREACH(rp, &lyp->nfsly_recall, nfsrecly_list) { + stateid.seqid = rp->nfsrecly_stateseqid; + (void)nfsrpc_layoutreturn(nmp, lyp->nfsly_fh, + lyp->nfsly_fhlen, 0, NFSLAYOUT_NFSV4_1_FILES, + rp->nfsrecly_iomode, rp->nfsrecly_recalltype, + rp->nfsrecly_off, rp->nfsrecly_len, + &stateid, 0, NULL, cred, p, NULL); + } +} + +/* + * Do the layout commit for a file layout. + */ +static void +nfscl_dolayoutcommit(struct nfsmount *nmp, struct nfscllayout *lyp, + struct ucred *cred, NFSPROC_T *p) +{ + int error; + + error = nfsrpc_layoutcommit(nmp, lyp->nfsly_fh, lyp->nfsly_fhlen, + 0, 0, 0, lyp->nfsly_lastbyte, &lyp->nfsly_stateid, + NFSLAYOUT_NFSV4_1_FILES, 0, NULL, cred, p, NULL); + if (error == NFSERR_NOTSUPP) { + /* If the server doesn't want it, don't bother doing it. */ + NFSLOCKMNT(nmp); + nmp->nm_state |= NFSSTA_NOLAYOUTCOMMIT; + NFSUNLOCKMNT(nmp); + } +} + +/* + * Commit all layouts for a file (vnode). + */ +int +nfscl_layoutcommit(vnode_t vp, NFSPROC_T *p) +{ + struct nfsclclient *clp; + struct nfscllayout *lyp; + struct nfsnode *np = VTONFS(vp); + mount_t mp; + struct nfsmount *nmp; + + mp = vnode_mount(vp); + nmp = VFSTONFS(mp); + if (NFSHASNOLAYOUTCOMMIT(nmp)) + return (0); + NFSLOCKCLSTATE(); + clp = nmp->nm_clp; + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + return (EPERM); + } + lyp = nfscl_findlayout(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (lyp == NULL) { + NFSUNLOCKCLSTATE(); + return (EPERM); + } + nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp); + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + NFSUNLOCKCLSTATE(); + return (EPERM); + } +tryagain: + if ((lyp->nfsly_flags & NFSLY_WRITTEN) != 0) { + lyp->nfsly_flags &= ~NFSLY_WRITTEN; + NFSUNLOCKCLSTATE(); + NFSCL_DEBUG(4, "do layoutcommit2\n"); + nfscl_dolayoutcommit(clp->nfsc_nmp, lyp, NFSPROCCRED(p), p); + NFSLOCKCLSTATE(); + goto tryagain; + } + nfsv4_relref(&lyp->nfsly_lock); + NFSUNLOCKCLSTATE(); + return (0); +} + |