Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 29 Dec 2012 02:10:55 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r244805 - in projects/nfsv4-packrats/sys: conf fs/nfs fs/nfsclient modules/nfscl nfs
Message-ID:  <201212290210.qBT2Atnd050518@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Sat Dec 29 02:10:55 2012
New Revision: 244805
URL: http://svnweb.freebsd.org/changeset/base/244805

Log:
  Put the current kernel patch for packrats in the project tree.

Added:
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clpackrat.c   (contents, props changed)
Modified:
  projects/nfsv4-packrats/sys/conf/files
  projects/nfsv4-packrats/sys/fs/nfs/nfs_commonport.c
  projects/nfsv4-packrats/sys/fs/nfs/nfs_var.h
  projects/nfsv4-packrats/sys/fs/nfs/nfsclstate.h
  projects/nfsv4-packrats/sys/fs/nfs/nfsport.h
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clbio.c
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clport.c
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clrpcops.c
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clstate.c
  projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clvnops.c
  projects/nfsv4-packrats/sys/fs/nfsclient/nfsnode.h
  projects/nfsv4-packrats/sys/modules/nfscl/Makefile
  projects/nfsv4-packrats/sys/nfs/nfs_nfssvc.c
  projects/nfsv4-packrats/sys/nfs/nfssvc.h

Modified: projects/nfsv4-packrats/sys/conf/files
==============================================================================
--- projects/nfsv4-packrats/sys/conf/files	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/conf/files	Sat Dec 29 02:10:55 2012	(r244805)
@@ -2388,6 +2388,7 @@ fs/nfsclient/nfs_clcomsubs.c	optional nf
 fs/nfsclient/nfs_clsubs.c	optional nfscl
 fs/nfsclient/nfs_clstate.c	optional nfscl
 fs/nfsclient/nfs_clkrpc.c	optional nfscl
+fs/nfsclient/nfs_clpackrat.c	optional nfscl
 fs/nfsclient/nfs_clrpcops.c	optional nfscl
 fs/nfsclient/nfs_clvnops.c	optional nfscl
 fs/nfsclient/nfs_clnode.c	optional nfscl

Modified: projects/nfsv4-packrats/sys/fs/nfs/nfs_commonport.c
==============================================================================
--- projects/nfsv4-packrats/sys/fs/nfs/nfs_commonport.c	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/fs/nfs/nfs_commonport.c	Sat Dec 29 02:10:55 2012	(r244805)
@@ -112,6 +112,7 @@ MALLOC_DEFINE(M_NEWNFSDEVINFO, "NFSCL de
 MALLOC_DEFINE(M_NEWNFSSOCKREQ, "NFSCL sockreq", "NFS Sock Req");
 MALLOC_DEFINE(M_NEWNFSCLDS, "NFSCL session", "NFSv4.1 Session");
 MALLOC_DEFINE(M_NEWNFSLAYRECALL, "NFSCL layrecall", "NFSv4.1 Layout Recall");
+MALLOC_DEFINE(M_NEWNFSLDIRTY, "NFSCL ldirty", "NFSCL deleg locally dirty");
 
 /*
  * Definition of mutex locks.

Modified: projects/nfsv4-packrats/sys/fs/nfs/nfs_var.h
==============================================================================
--- projects/nfsv4-packrats/sys/fs/nfs/nfs_var.h	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/fs/nfs/nfs_var.h	Sat Dec 29 02:10:55 2012	(r244805)
@@ -522,7 +522,7 @@ int nfscl_renamedeleg(vnode_t, nfsv4stat
 void nfscl_reclaimnode(vnode_t);
 void nfscl_newnode(vnode_t);
 void nfscl_delegmodtime(vnode_t);
-void nfscl_deleggetmodtime(vnode_t, struct timespec *);
+void nfscl_deleggetmod(vnode_t, struct timespec *, uint64_t *);
 int nfscl_tryclose(struct nfsclopen *, struct ucred *,
     struct nfsmount *, NFSPROC_T *);
 void nfscl_cleanup(NFSPROC_T *);
@@ -541,6 +541,7 @@ void nfscl_freelayout(struct nfscllayout
 void nfscl_freeflayout(struct nfsclflayout *);
 void nfscl_freedevinfo(struct nfscldevinfo *);
 int nfscl_layoutcommit(vnode_t, NFSPROC_T *);
+struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, int);
 
 /* nfs_clport.c */
 int nfscl_nget(mount_t, vnode_t, struct nfsfh *,
@@ -655,3 +656,16 @@ void nfsrvd_init(int);
 int nfscbd_addsock(struct file *);
 int nfscbd_nfsd(NFSPROC_T *, struct nfsd_nfscbd_args *);
 
+/* nfs_clpackrat.c */
+void nfscl_packratsetup(struct nfscldeleg *, struct nfsmount *, struct ucred *,
+    NFSPROC_T *);
+void nfscl_packratopen(vnode_t, NFSPROC_T *);
+void nfscl_packratclose(vnode_t, NFSPROC_T *);
+int nfscbd_packrat(char *);
+int nfscl_packratread(vnode_t, struct uio *, int, struct ucred *, int *);
+int nfscl_packratwrite(vnode_t, struct uio *, int, struct ucred *,
+    NFSPROC_T *, int *);
+int nfscl_deleglocalflush(struct nfscldeleg *, struct nfsmount *, NFSPROC_T *,
+    int, int);
+void nfscl_packratsetsize(vnode_t, uint64_t);
+

Modified: projects/nfsv4-packrats/sys/fs/nfs/nfsclstate.h
==============================================================================
--- projects/nfsv4-packrats/sys/fs/nfs/nfsclstate.h	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/fs/nfs/nfsclstate.h	Sat Dec 29 02:10:55 2012	(r244805)
@@ -136,6 +136,15 @@ struct nfsclowner {
 };
 
 /*
+ * List entry for dirty byte ranges for nfscldeleg.
+ */
+struct nfsldirty {
+	LIST_ENTRY(nfsldirty)	nfsw_list;
+	uint64_t		nfsw_first;
+	uint64_t		nfsw_end;
+};
+
+/*
  * MALLOC'd to the correct length to accommodate the file handle.
  */
 struct nfscldeleg {
@@ -153,21 +162,41 @@ struct nfscldeleg {
 	u_int64_t		nfsdl_size;	/* saved copy of file size */
 	u_int64_t		nfsdl_change;	/* and change attribute */
 	struct timespec		nfsdl_modtime;	/* local modify time */
+	u_int64_t		nfsdl_opensize;	/* size at open */
+	u_int64_t		nfsdl_localsize;/* locally stored file size */
+	LIST_HEAD(, nfsldirty)	nfsdl_ldirty;	/* locally mod. ranges */
+	u_int32_t		nfsdl_localiocnt; /* Local I/Os in prog */
+	vnode_t			nfsdl_filevp;	/* Local copy of file */
+	vnode_t			nfsdl_delegvp;	/* Delegation record file */
+	struct proc		*nfsdl_packratthread; /* Packrat copy thread */
 	u_int16_t		nfsdl_fhlen;
-	u_int8_t		nfsdl_flags;
+	u_int16_t		nfsdl_flags;
 	u_int8_t		nfsdl_fh[1];	/* must be last */
 };
 
 /*
  * nfsdl_flags bits.
  */
-#define	NFSCLDL_READ		0x01
-#define	NFSCLDL_WRITE		0x02
-#define	NFSCLDL_RECALL		0x04
-#define	NFSCLDL_NEEDRECLAIM	0x08
-#define	NFSCLDL_ZAPPED		0x10
-#define	NFSCLDL_MODTIMESET	0x20
-#define	NFSCLDL_DELEGRET	0x40
+#define	NFSCLDL_READ		0x0001
+#define	NFSCLDL_WRITE		0x0002
+#define	NFSCLDL_RECALL		0x0004
+#define	NFSCLDL_NEEDRECLAIM	0x0008
+#define	NFSCLDL_ZAPPED		0x0010
+#define	NFSCLDL_MODTIMESET	0x0020
+#define	NFSCLDL_DELEGRET	0x0040
+#define	NFSCLDL_COPYINPROG	0x0080
+#define	NFSCLDL_LOCALSIZESET	0x0100
+#define	NFSCLDL_HASCOPY		0x0200
+#define	NFSCLDL_WAITRECALL	0x0400
+
+/*
+ * Maximum length of a local filename used by the packrat daemons.
+ * 1 - indicates the type of file.
+ * 1 - indicates IPv4 vs IPv6 host address.
+ * 32 - maximum IP host address length.
+ * 174 - the maximum length of the file handle in ascii.
+ */
+#define	NFSPCKRAT_MAXFILELEN	(1 + 1 + 32 + 174)
 
 /*
  * MALLOC'd to the correct length to accommodate the file handle.

Modified: projects/nfsv4-packrats/sys/fs/nfs/nfsport.h
==============================================================================
--- projects/nfsv4-packrats/sys/fs/nfs/nfsport.h	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/fs/nfs/nfsport.h	Sat Dec 29 02:10:55 2012	(r244805)
@@ -743,6 +743,7 @@ MALLOC_DECLARE(M_NEWNFSDEVINFO);
 MALLOC_DECLARE(M_NEWNFSSOCKREQ);
 MALLOC_DECLARE(M_NEWNFSCLDS);
 MALLOC_DECLARE(M_NEWNFSLAYRECALL);
+MALLOC_DECLARE(M_NEWNFSLDIRTY);
 #define	M_NFSRVCACHE	M_NEWNFSRVCACHE
 #define	M_NFSDCLIENT	M_NEWNFSDCLIENT
 #define	M_NFSDSTATE	M_NEWNFSDSTATE
@@ -768,6 +769,7 @@ MALLOC_DECLARE(M_NEWNFSLAYRECALL);
 #define	M_NFSSOCKREQ	M_NEWNFSSOCKREQ
 #define	M_NFSCLDS	M_NEWNFSCLDS
 #define	M_NFSLAYRECALL	M_NEWNFSLAYRECALL
+#define	M_NFSLDIRTY	M_NEWNFSLDIRTY
 
 #define	NFSINT_SIGMASK(set) 						\
 	(SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) ||	\

Modified: projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clbio.c
==============================================================================
--- projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clbio.c	Sat Dec 29 00:30:30 2012	(r244804)
+++ projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clbio.c	Sat Dec 29 02:10:55 2012	(r244805)
@@ -82,7 +82,7 @@ static int nfs_directio_write(struct vno
 int
 ncl_getpages(struct vop_getpages_args *ap)
 {
-	int i, error, nextoff, size, toff, count, npages;
+	int i, error, nextoff, size, toff, count, npages, didread;
 	struct uio uio;
 	struct iovec iov;
 	vm_offset_t kva;
@@ -169,7 +169,9 @@ ncl_getpages(struct vop_getpages_args *a
 	uio.uio_rw = UIO_READ;
 	uio.uio_td = td;
 
-	error = ncl_readrpc(vp, &uio, cred);
+	error = nfscl_packratread(vp, &uio, 0, cred, &didread);
+	if (didread == 0)
+		error = ncl_readrpc(vp, &uio, cred);
 	pmap_qremove(kva, npages);
 
 	relpbuf(bp, &ncl_pbuf_freecnt);
@@ -245,7 +247,7 @@ ncl_putpages(struct vop_putpages_args *a
 	struct iovec iov;
 	vm_offset_t kva;
 	struct buf *bp;
-	int iomode, must_commit, i, error, npages, count;
+	int iomode, must_commit, i, error, npages, count, didwrite;
 	off_t offset;
 	int *rtvals;
 	struct vnode *vp;
@@ -325,7 +327,9 @@ ncl_putpages(struct vop_putpages_args *a
 	else
 	    iomode = NFSWRITE_FILESYNC;
 
-	error = ncl_writerpc(vp, &uio, cred, &iomode, &must_commit, 0);
+	error = nfscl_packratwrite(vp, &uio, 0, cred, NULL, &didwrite);
+	if (didwrite == 0)
+		error = ncl_writerpc(vp, &uio, cred, &iomode, &must_commit, 0);
 	crfree(cred);
 
 	pmap_qremove(kva, npages);
@@ -873,7 +877,7 @@ ncl_write(struct vop_write_args *ap)
 	struct vattr vattr;
 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
 	daddr_t lbn;
-	int bcount;
+	int bcount, didwrite;
 	int bp_cached, n, on, error = 0, error1;
 	size_t orig_resid, local_resid;
 	off_t orig_size, tmp_off;
@@ -902,6 +906,14 @@ ncl_write(struct vop_write_args *ap)
 	mtx_unlock(&nmp->nm_mtx);
 
 	/*
+	 * Do the write locally if there is a write delegation and packrats
+	 * have created a locally cached copy.
+	 */
+	error = nfscl_packratwrite(vp, uio, ioflag, cred, td, &didwrite);
+	if (didwrite != 0)
+		return (error);
+
+	/*
 	 * Synchronously flush pending buffers if we are in synchronous
 	 * mode or if we are appending.
 	 */
@@ -1374,7 +1386,7 @@ ncl_vinvalbuf(struct vnode *vp, int flag
 	if (NFSHASPNFS(nmp))
 		nfscl_layoutcommit(vp, td);
 	mtx_lock(&np->n_mtx);
-	if (np->n_directio_asyncwr == 0)
+	if (np->n_directio_asyncwr == 0 && (np->n_flag & NLOCALCACHE) == 0)
 		np->n_flag &= ~NMODIFIED;
 	mtx_unlock(&np->n_mtx);
 out:
@@ -1548,7 +1560,8 @@ ncl_doio_directwrite(struct buf *bp)
 		mtx_lock(&np->n_mtx);
 		np->n_directio_asyncwr--;
 		if (np->n_directio_asyncwr == 0) {
-			np->n_flag &= ~NMODIFIED;
+			if ((np->n_flag & NLOCALCACHE) == 0)
+				np->n_flag &= ~NMODIFIED;
 			if ((np->n_flag & NFSYNCWAIT)) {
 				np->n_flag &= ~NFSYNCWAIT;
 				wakeup((caddr_t)&np->n_directio_asyncwr);

Added: projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clpackrat.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/nfsv4-packrats/sys/fs/nfsclient/nfs_clpackrat.c	Sat Dec 29 02:10:55 2012	(r244805)
@@ -0,0 +1,1211 @@
+/*-
+ * Copyright (c) 2009 Rick Macklem, University of Guelph
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <fs/nfs/nfsport.h>
+#include <fs/nfsclient/nfs.h>
+
+/*
+ * These functions provide the packrat support for the NFSv4 client. That
+ * is, they implement agressive client side whole file caching to a directory
+ * on stable storage, such as a disk.
+ */
+#ifndef APPLEKEXT
+NFSCLSTATEMUTEX;
+static char nfscl_packratpath[MAXPATHLEN + 1];
+static int nfscl_packratpathlen = 0;
+static uint64_t nfscl_packratmaxsize = 10000000;
+#endif	/* !APPLEKEXT */
+
+#define	NFS_PACKRATIOSIZE	NFS_MAXBSIZE
+
+static vnode_t nfscl_openfile(char *, boolean_t, struct ucred *, NFSPROC_T *);
+static void nfscl_closefile(vnode_t, char *, boolean_t, struct ucred *,
+    NFSPROC_T *);
+static void start_packrat(void *);
+static void nfscl_start_packratthread(struct nfscldeleg *);
+static void nfscl_packratthread(struct nfscldeleg *, NFSPROC_T *);
+static int nfsrpc_readdp(struct nfscldeleg *, struct uio *, struct ucred *,
+    NFSPROC_T *);
+static int nfsrpc_readrpcdp(struct nfscldeleg *, struct uio *, struct ucred *,
+    NFSPROC_T *);
+static void nfscl_fhtofilename(u_int8_t *, u_int16_t, char *);
+static void nfscl_packratbreakdown(struct nfscldeleg *, struct nfsmount *,
+    struct ucred *, NFSPROC_T *);
+static void nfscl_updatewrite(struct nfscldeleg *, struct nfsldirty *);
+static int nfscl_localwrite(struct nfscldeleg *, uint64_t, int,
+    struct ucred *, struct ucred *, NFSPROC_T *, int);
+static int nfsrpc_writedp(struct nfscldeleg *, uint8_t *, off_t, int,
+    struct ucred *, NFSPROC_T *, int);
+static int nfsrpc_writerpcdp(struct nfscldeleg *, uint8_t *, off_t, int,
+    struct ucred *, NFSPROC_T *);
+static void nfscl_truncdirty(struct nfscldeleg *, uint64_t);
+static int nfscl_packrathostaddr(struct nfsmount *, char *, int);
+static void nfscl_packratgetvp(struct nfscldeleg *, struct nfsmount *, char *,
+    int, struct ucred *, NFSPROC_T *);
+
+/*
+ * This function opens/creates a file for reading and writing, returning the
+ * vnode pointer for it.
+ */
+static vnode_t
+nfscl_openfile(char *path, boolean_t createit, struct ucred *cred, NFSPROC_T *p)
+{
+	struct nameidata nd;
+	int error, fmode;
+
+	NDINIT_AT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE,
+	    path, AT_FDCWD, p);
+	if (createit)
+		fmode = O_CREAT | O_TRUNC | O_RDWR;
+	else
+		fmode = O_RDWR;
+	error = vn_open_cred(&nd, &fmode, 0600, VN_OPEN_NOAUDIT, cred, NULL);
+	if (error == 0)
+		NDFREE(&nd, NDF_ONLY_PNBUF);
+	else if (nd.ni_vp != NULL)
+		panic("nfsclopenf");
+	return (nd.ni_vp);
+}
+
+/*
+ * This function closes and removes a file that was previously created/opened
+ * by nfscl_openfile().
+ */
+static void
+nfscl_closefile(vnode_t vp, char *path, boolean_t unlinkit, struct ucred *cred,
+    NFSPROC_T *p)
+{
+
+	(void) vn_close(vp, FREAD | FWRITE, cred, p);
+	if (unlinkit)
+		(void) kern_unlinkat(p, AT_FDCWD, path, UIO_SYSSPACE, 0);
+}
+
+/*
+ * Start up a packrat thread to copy the file to local disk.
+ */
+static void
+start_packrat(void *arg)
+{
+	struct nfscldeleg *dp;
+	struct thread *td;
+
+	dp = (struct nfscldeleg *)arg;
+	td = TAILQ_FIRST(&dp->nfsdl_packratthread->p_threads);
+	nfscl_packratthread(dp, td);
+	kproc_exit(0);
+}
+
+static void
+nfscl_start_packratthread(struct nfscldeleg *dp)
+{
+
+	kproc_create(start_packrat, (void *)dp, &dp->nfsdl_packratthread, 0, 0,
+	    "nfspackrat");
+}
+
+/*
+ * This is the body of the packrat copy thread. Just copy the file to the
+ * local one on disk.
+ */
+static void
+nfscl_packratthread(struct nfscldeleg *dp, NFSPROC_T *td)
+{
+	struct uio uio;
+	struct iovec io;
+	struct ucred *incred, *outcred;
+	char *iobuf;
+	int resid, error;
+	off_t off;
+
+	incred = newnfs_getcred();
+	outcred = newnfs_getcred();
+	newnfs_copycred(&dp->nfsdl_cred, incred);
+	iobuf = (char *)malloc(NFS_PACKRATIOSIZE, M_TEMP, M_WAITOK);
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_offset = 0;
+
+	/* Loop around until eof */
+	do {
+		off = uio.uio_offset;
+		uio.uio_resid = NFS_PACKRATIOSIZE;
+		io.iov_len = NFS_PACKRATIOSIZE;
+		io.iov_base = iobuf;
+		uio.uio_iovcnt = 1;
+		uio.uio_iov = &io;
+		uio.uio_td = td;
+		error = nfsrpc_readdp(dp, &uio, incred, td);
+
+		/* and then write to the disk file */
+		if (error == 0 && uio.uio_resid < NFS_PACKRATIOSIZE) {
+			error = vn_rdwr(UIO_WRITE, dp->nfsdl_filevp,
+			    iobuf, NFS_PACKRATIOSIZE - uio.uio_resid,
+			    off, UIO_SYSSPACE, IO_NOMACCHECK, outcred,
+			    NOCRED, &resid, td);
+			if (error == 0) {
+				dp->nfsdl_localsize = uio.uio_offset;
+				wakeup(&dp->nfsdl_localsize);
+			}
+		}
+	} while (error == 0 && uio.uio_resid < NFS_PACKRATIOSIZE);
+	free(iobuf, M_TEMP);
+
+	if (error == 0) {
+		vnode_pager_setsize(dp->nfsdl_filevp, dp->nfsdl_localsize);
+		NFSLOCKCLSTATE();
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		dp->nfsdl_flags |= NFSCLDL_HASCOPY;
+		wakeup(&dp->nfsdl_flags);
+		wakeup(&dp->nfsdl_localsize);
+		NFSUNLOCKCLSTATE();
+	} else
+		nfscl_packratbreakdown(dp, dp->nfsdl_clp->nfsc_nmp, outcred,
+		    td);
+	NFSFREECRED(incred);
+	NFSFREECRED(outcred);
+}
+
+/*
+ * Read operation against an NFSv4 delegation.
+ */
+static int
+nfsrpc_readdp(struct nfscldeleg *dp, struct uio *uiop, struct ucred *cred,
+    NFSPROC_T *p)
+{
+	int error, expireret = 0, retrycnt;
+	u_int32_t clidrev = 0;
+
+	clidrev = dp->nfsdl_clp->nfsc_clientidrev;
+	retrycnt = 0;
+	do {
+		error = nfsrpc_readrpcdp(dp, uiop, cred, p);
+		if (error == NFSERR_STALESTATEID)
+			nfscl_initiate_recovery(dp->nfsdl_clp);
+		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
+		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
+		    error == NFSERR_OLDSTATEID) {
+			(void) nfs_catnap(PZERO, error, "nfs_readdp");
+		} else if ((error == NFSERR_EXPIRED ||
+		    error == NFSERR_BADSTATEID) && clidrev != 0) {
+			expireret = nfscl_hasexpired(dp->nfsdl_clp, clidrev, p);
+		}
+		retrycnt++;
+	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
+	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
+	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
+	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
+	     expireret == 0 && clidrev != 0 && retrycnt < 4));
+	if (error && retrycnt >= 4)
+		error = EIO;
+	return (error);
+}
+
+/*
+ * The actual read RPC for the above.
+ */
+static int
+nfsrpc_readrpcdp(struct nfscldeleg *dp, struct uio *uiop, struct ucred *cred,
+    NFSPROC_T *p)
+{
+	u_int32_t *tl;
+	int error = 0, len, retlen, tsiz, eof = 0;
+	struct nfsrv_descript nfsd;
+	struct nfsrv_descript *nd = &nfsd;
+	struct nfsmount *nmp;
+
+	nmp = dp->nfsdl_clp->nfsc_nmp;
+	if (nmp == NULL)
+		return (0);
+	tsiz = uio_uio_resid(uiop);
+	nd->nd_mrep = NULL;
+	while (tsiz > 0) {
+		len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
+		nfscl_reqstart(nd, NFSPROC_READ, nmp, dp->nfsdl_fh,
+		    dp->nfsdl_fhlen, NULL, NULL);
+		nfsm_stateidtom(nd, &dp->nfsdl_stateid, NFSSTATEID_PUTSTATEID);
+		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
+		txdr_hyper(uiop->uio_offset, tl);
+		*(tl + 2) = txdr_unsigned(len);
+		error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL,
+		    p, cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
+		if (error != 0)
+			return (error);
+		if (nd->nd_repstat != 0 || error != 0) {
+			if (error == 0)
+				error = nd->nd_repstat;
+			goto nfsmout;
+		}
+		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+		eof = fxdr_unsigned(int, *tl);
+		NFSM_STRSIZ(retlen, nmp->nm_rsize);
+		error = nfsm_mbufuio(nd, uiop, retlen);
+		if (error)
+			goto nfsmout;
+		mbuf_freem(nd->nd_mrep);
+		nd->nd_mrep = NULL;
+		tsiz -= retlen;
+		if (eof || retlen == 0)
+			tsiz = 0;
+	}
+	return (0);
+nfsmout:
+	if (nd->nd_mrep != NULL)
+		mbuf_freem(nd->nd_mrep);
+	return (error);
+}
+
+static u_int8_t *not64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$%";
+static u_int8_t *hexval = "0123456789abcdef";
+/*
+ * This function generates a file name for the file handle passed in as an
+ * argument. Since the file handle can be up to 128 bytes and a file name is
+ * limited to 255 in length, a simple hexadecimal representation doesn't
+ * work.
+ * The name looks like this:
+ * - 2 hex digits for the file handle length
+ * - fhlen/3 sequences of four characters, where each ascii character
+ *   represents 6 bits (4 digits representing the 3 bytes)
+ * - 0 to 2 bytes represented as hexadecimal
+ * As such, a 128 byte fh creates a 174 byte file name.
+ * The name argument must point to sufficient storage.
+ */
+static void
+nfscl_fhtofilename(u_int8_t *fh, u_int16_t fhlen, char *name)
+{
+
+	*name++ = hexval[fhlen & 0xf];
+	*name++ = hexval[fhlen >> 4];
+
+	/*
+	 * Now loop around for the fh bytes until < 3 left.
+	 */
+	while (fhlen >= 3) {
+		*name++ = not64[*fh & 0x3f];
+		*name++ = not64[((*fh & 0xc0) >> 2) | (*(fh + 1) & 0xf)];
+		*name++ = not64[(*(fh + 1) >> 4) | ((*(fh + 2) & 0xc0) >> 2)];
+		*name++ = not64[*(fh + 2) & 0x3f];
+		fh += 3;
+		fhlen -= 3;
+	}
+
+	/*
+	 * and then do the last 0 to 2 bytes as hexadecimal.
+	 */
+	while (fhlen > 0) {
+		*name++ = hexval[*fh & 0xf];
+		*name++ = hexval[*fh >> 4];
+		fh++;
+		fhlen--;
+	}
+	*name = '\0';
+}
+
+/*
+ * Set up a new delegation for packrat support.
+ */
+void
+nfscl_packratsetup(struct nfscldeleg *dp, struct nfsmount *nmp,
+    struct ucred *cred, NFSPROC_T *p)
+{
+	vnode_t delegvp, filevp;
+	char fname[MAXPATHLEN + 1];
+	int pathlen, pathlen2;
+
+	NFSLOCKCLSTATE();
+	if (nfscl_packratpathlen == 0 ||
+	    dp->nfsdl_opensize > nfscl_packratmaxsize) {
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		wakeup(&dp->nfsdl_flags);
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	pathlen = nfscl_packratpathlen;
+	bcopy(nfscl_packratpath, &fname[0], pathlen);
+	NFSUNLOCKCLSTATE();
+
+	/*
+	 * The appropriate fields of the delegation structure are already
+	 * initialized, so create the files and, if that is successful,
+	 * fire up a packrat thread for it.
+	 */
+	pathlen2 = pathlen;
+	fname[pathlen++] = 'D';
+	pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen);
+	if (pathlen == 0) {
+		NFSLOCKCLSTATE();
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		wakeup(&dp->nfsdl_flags);
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen, &fname[pathlen]);
+	delegvp = nfscl_openfile(fname, TRUE, cred, p);
+	if (delegvp == NULL) {
+		NFSLOCKCLSTATE();
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		wakeup(&dp->nfsdl_flags);
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	VOP_UNLOCK(delegvp, 0);
+	fname[pathlen2] = 'F';
+	filevp = nfscl_openfile(fname, TRUE, cred, p);
+	if (filevp == NULL) {
+		nfscl_closefile(delegvp, fname, TRUE, cred, p);
+		NFSLOCKCLSTATE();
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		wakeup(&dp->nfsdl_flags);
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	VOP_UNLOCK(filevp, 0);
+
+	NFSLOCKCLSTATE();
+	dp->nfsdl_delegvp = delegvp;
+	dp->nfsdl_filevp = filevp;
+	dp->nfsdl_localsize = 0;
+	NFSUNLOCKCLSTATE();
+
+	nfscl_start_packratthread(dp);
+}
+
+/*
+ * Reopen the locally cached copy of the file, created by nfscl_packratsetup().
+ */
+void
+nfscl_packratopen(vnode_t vp, NFSPROC_T *p)
+{
+	struct nfsclclient *clp;
+	struct nfscldeleg *dp;
+	struct nfsnode *np = VTONFS(vp);
+	struct nfsmount *nmp;
+	struct ucred *cred;
+	char fname[MAXPATHLEN + 1];
+	int pathlen;
+
+	nmp = VFSTONFS(vnode_mount(vp));
+	NFSLOCKCLSTATE();
+	if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	clp = nfscl_findcl(nmp);
+	if (clp == NULL) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
+	if (dp == NULL || (dp->nfsdl_flags &
+	    (NFSCLDL_RECALL | NFSCLDL_WRITE | NFSCLDL_HASCOPY)) !=
+	    (NFSCLDL_WRITE | NFSCLDL_HASCOPY) ||
+	    dp->nfsdl_filevp != NULL) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+
+	/* Now, try and reopen them. */
+	pathlen = nfscl_packratpathlen;
+	bcopy(nfscl_packratpath, &fname[0], pathlen);
+	NFSUNLOCKCLSTATE();
+
+	cred = newnfs_getcred();
+	nfscl_packratgetvp(dp, nmp, fname, pathlen, cred, p);
+	NFSFREECRED(cred);
+}
+
+/*
+ * Do the actual reopens to get the vnodes.
+ */
+static void
+nfscl_packratgetvp(struct nfscldeleg *dp, struct nfsmount *nmp, char *fname,
+    int pathlen, struct ucred *cred, NFSPROC_T *p)
+{
+	vnode_t delegvp, filevp;
+	boolean_t closethem;
+	int pathlen2;
+
+	/*
+	 * The appropriate fields of the delegation structure are already
+	 * initialized, so create the files and, if that is successful,
+	 * fire up a packrat thread for it.
+	 */
+	pathlen2 = pathlen;
+	fname[pathlen++] = 'D';
+	pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen);
+	if (pathlen == 0)
+		return;
+	nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen, &fname[pathlen]);
+	delegvp = nfscl_openfile(fname, FALSE, cred, p);
+	if (delegvp == NULL)
+		return;
+	VOP_UNLOCK(delegvp, 0);
+	fname[pathlen2] = 'F';
+	filevp = nfscl_openfile(fname, FALSE, cred, p);
+	if (filevp == NULL) {
+		nfscl_closefile(delegvp, fname, FALSE, cred, p);
+		return;
+	}
+	VOP_UNLOCK(filevp, 0);
+
+	NFSLOCKCLSTATE();
+	if (dp->nfsdl_delegvp == NULL) {
+		dp->nfsdl_delegvp = delegvp;
+		dp->nfsdl_filevp = filevp;
+		closethem = FALSE;
+	} else
+		closethem = TRUE;
+	NFSUNLOCKCLSTATE();
+
+	if (closethem) {
+		fname[pathlen2] = 'D';
+		nfscl_closefile(delegvp, fname, FALSE, cred, p);
+		fname[pathlen2] = 'F';
+		nfscl_closefile(filevp, fname, FALSE, cred, p);
+	}
+}
+
+/*
+ * Get rid of the local files related to a delegation.
+ */
+static void
+nfscl_packratbreakdown(struct nfscldeleg *dp, struct nfsmount *nmp,
+    struct ucred *cred, NFSPROC_T *p)
+{
+	vnode_t delegvp, filevp;
+	char fname[MAXPATHLEN + 1];
+	int pathlen, pathlen2;
+
+	/*
+	 * Get the vnode pointers out of the delegation structure and
+	 * null them out.
+	 */
+	NFSLOCKCLSTATE();
+	pathlen = nfscl_packratpathlen;
+	if (pathlen > 0)
+		bcopy(nfscl_packratpath, &fname[0], pathlen);
+	delegvp = dp->nfsdl_delegvp;
+	filevp = dp->nfsdl_filevp;
+	dp->nfsdl_delegvp = NULL;
+	dp->nfsdl_filevp = NULL;
+	pathlen2 = pathlen;
+	if (pathlen > 0) {
+		fname[pathlen++] = 'D';
+		pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen);
+	}
+	if (pathlen > 0)
+		nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen,
+		    &fname[pathlen]);
+	while (dp->nfsdl_localiocnt > 0)
+		(void) nfsmsleep(&dp->nfsdl_localiocnt, NFSCLSTATEMUTEXPTR,
+		    PZERO, "nfspckbr", NULL);
+	if ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0) {
+		dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG;
+		wakeup(&dp->nfsdl_flags);
+	}
+	NFSUNLOCKCLSTATE();
+	if (pathlen == 0)
+		return;
+
+	/*
+	 * Close/remove the 2 files.
+	 */
+	if (delegvp != NULL)
+		nfscl_closefile(delegvp, fname, TRUE, cred, p);
+	fname[pathlen2] = 'F';
+	if (filevp != NULL)
+		nfscl_closefile(filevp, fname, TRUE, cred, p);
+}
+
+/*
+ * Close the local files related to a delegation in order to release the
+ * vnodes.
+ */
+void
+nfscl_packratclose(vnode_t vp, NFSPROC_T *p)
+{
+	struct nfsclclient *clp;
+	struct nfscldeleg *dp;
+	struct nfsnode *np = VTONFS(vp);
+	struct nfsmount *nmp;
+	struct ucred *cred;
+	vnode_t delegvp, filevp;
+	char fname[MAXPATHLEN + 1];
+	int pathlen, pathlen2;
+
+	nmp = VFSTONFS(vnode_mount(vp));
+	NFSLOCKCLSTATE();
+	if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0 ||
+	    vp->v_usecount > 1) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	clp = nfscl_findcl(nmp);
+	if (clp == NULL) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
+	if (dp == NULL || (dp->nfsdl_flags &
+	    (NFSCLDL_RECALL | NFSCLDL_WRITE | NFSCLDL_HASCOPY)) !=
+	    (NFSCLDL_WRITE | NFSCLDL_HASCOPY) ||
+	    dp->nfsdl_filevp == NULL) {
+		NFSUNLOCKCLSTATE();
+		return;
+	}
+	pathlen = nfscl_packratpathlen;
+	bcopy(nfscl_packratpath, &fname[0], pathlen);
+	delegvp = dp->nfsdl_delegvp;
+	filevp = dp->nfsdl_filevp;
+	dp->nfsdl_delegvp = NULL;
+	dp->nfsdl_filevp = NULL;
+	while (dp->nfsdl_localiocnt > 0)
+		(void) nfsmsleep(&dp->nfsdl_localiocnt, NFSCLSTATEMUTEXPTR,
+		    PZERO, "nfspckbr", NULL);
+	NFSUNLOCKCLSTATE();
+	pathlen2 = pathlen;
+	fname[pathlen++] = 'D';
+	pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen);
+	if (pathlen == 0)
+		return;
+	nfscl_fhtofilename(np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
+	    &fname[pathlen]);
+
+	/*
+	 * Close the 2 files.
+	 */
+	cred = newnfs_getcred();
+	nfscl_closefile(delegvp, fname, FALSE, cred, p);
+	fname[pathlen2] = 'F';
+	nfscl_closefile(filevp, fname, FALSE, cred, p);
+	NFSFREECRED(cred);
+}
+
+/*
+ * Set up the packrat directory.
+ */
+int
+nfscbd_packrat(char *pathbuf)
+{
+	int pathlen;
+
+	pathlen = strlen(pathbuf);
+	if (pathlen < 1 || pathbuf[pathlen - 1] != '/')
+		return (EINVAL);
+	NFSLOCKCLSTATE();
+	strcpy(nfscl_packratpath, pathbuf);
+	nfscl_packratpathlen = pathlen;
+	NFSUNLOCKCLSTATE();
+	return (0);
+}
+
+/*
+ * Do a read via a local cached copy, if possible.
+ * Return whether or not the local read was possible via "didread".
+ */
+int
+nfscl_packratread(vnode_t vp, struct uio *uio, int ioflag, struct ucred *cred,
+    int *didread)
+{
+	struct nfsclclient *clp;
+	struct nfscldeleg *dp;
+	struct nfsnode *np = VTONFS(vp);
+	struct nfsmount *nmp;
+	vnode_t filevp;
+	int error;
+	ssize_t tresid;
+
+	*didread = 0;
+	nmp = VFSTONFS(vnode_mount(vp));
+	NFSLOCKCLSTATE();
+	if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) {
+		NFSUNLOCKCLSTATE();
+		return (0);
+	}
+	clp = nfscl_findcl(nmp);
+	if (clp == NULL) {
+		NFSUNLOCKCLSTATE();
+		return (0);
+	}
+	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
+	if (dp == NULL || (dp->nfsdl_flags &
+	    (NFSCLDL_RECALL | NFSCLDL_WRITE)) != NFSCLDL_WRITE ||
+	    dp->nfsdl_filevp == NULL ||
+	    ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0 &&
+	     uio->uio_offset + uio->uio_resid > dp->nfsdl_localsize)) {
+		if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) != 0) {
+			dp->nfsdl_flags |= NFSCLDL_WAITRECALL;
+			(void) nfsmsleep(&dp->nfsdl_ldirty, NFSCLSTATEMUTEXPTR,
+			    PZERO, "nfspkrc", NULL);
+		}
+		NFSUNLOCKCLSTATE();
+		NFSLOCKNODE(np);
+		NFSINVALATTRCACHE(np);
+		np->n_flag &= ~NLOCALCACHE;
+		NFSUNLOCKNODE(np);
+		return (0);
+	}
+
+	*didread = 1;
+	if (uio->uio_offset >= dp->nfsdl_localsize) {
+		NFSUNLOCKCLSTATE();
+		return (0);
+	}
+	if (uio->uio_offset + uio->uio_resid > dp->nfsdl_localsize) {
+		tresid = uio->uio_resid;
+		uio->uio_resid = dp->nfsdl_localsize - uio->uio_offset;
+		tresid -= uio->uio_resid;
+	} else
+		tresid = 0;
+	dp->nfsdl_localiocnt++;
+	filevp = dp->nfsdl_filevp;
+	NFSUNLOCKCLSTATE();
+	error = 0;
+	vn_lock(filevp, LK_SHARED | LK_RETRY);
+	VI_LOCK(filevp);
+	if ((filevp->v_iflag & VI_DOOMED) != 0)
+		error = ENOENT;
+	VI_UNLOCK(filevp);
+	if (error == 0)
+		error = VOP_READ(filevp, uio, ioflag, cred);
+	VOP_UNLOCK(filevp, 0);
+	uio->uio_resid += tresid;
+	NFSLOCKCLSTATE();
+	dp->nfsdl_localiocnt--;
+	if (dp->nfsdl_localiocnt == 0)
+		wakeup(&dp->nfsdl_localiocnt);
+	NFSUNLOCKCLSTATE();
+	return (error);
+}
+
+/*
+ * Do a write into a local copy cached on disk, if possible.
+ * Return whether or not the local write was possible via "didwrite".
+ */
+int
+nfscl_packratwrite(vnode_t vp, struct uio *uio, int ioflag, struct ucred *cred,
+    NFSPROC_T *p, int *didwrite)
+{
+	struct nfsclclient *clp;
+	struct nfscldeleg *dp;
+	struct nfsnode *np = VTONFS(vp);
+	struct nfsmount *nmp;
+	struct mount *mp;
+	struct nfsldirty *wp;
+	vnode_t filevp;
+	uint64_t end;
+	int error;
+	off_t setsize;
+
+	*didwrite = 0;
+	nmp = VFSTONFS(vnode_mount(vp));
+	NFSLOCKCLSTATE();
+	if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) {
+		NFSUNLOCKCLSTATE();
+		NFSLOCKNODE(np);
+		np->n_flag &= ~NLOCALCACHE;
+		NFSUNLOCKNODE(np);
+		return (0);
+	}
+	clp = nfscl_findcl(nmp);
+	if (clp == NULL) {
+		NFSUNLOCKCLSTATE();
+		NFSLOCKNODE(np);
+		np->n_flag &= ~NLOCALCACHE;
+		NFSUNLOCKNODE(np);
+		return (0);
+	}
+	dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
+	if (dp == NULL || (dp->nfsdl_flags &
+	    (NFSCLDL_RECALL | NFSCLDL_WRITE)) != NFSCLDL_WRITE ||
+	    dp->nfsdl_filevp == NULL) {
+		if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) != 0) {
+			dp->nfsdl_flags |= NFSCLDL_WAITRECALL;
+			(void) nfsmsleep(&dp->nfsdl_ldirty, NFSCLSTATEMUTEXPTR,
+			    PZERO, "nfspkwc", NULL);
+		}
+		NFSUNLOCKCLSTATE();
+		NFSLOCKNODE(np);
+		np->n_flag &= ~NLOCALCACHE;
+		NFSUNLOCKNODE(np);
+		return (0);
+	}
+
+	/*
+	 * We can now try and do the write. It cannot be done until the
+	 * local copy has been read in to past the point at which we
+	 * are writing, so we must loop until enough reading has been
+	 * completed.
+	 */
+	end = (uint64_t)uio->uio_offset + uio->uio_resid;
+	while ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0 &&
+	    (end > dp->nfsdl_localsize || (ioflag & IO_APPEND) != 0))
+		(void) nfsmsleep(&dp->nfsdl_localsize, NFSCLSTATEMUTEXPTR,
+		    PZERO, "nfspckw", NULL);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201212290210.qBT2Atnd050518>