diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 288bc76b4574..e2420d9f5db9 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -12,6 +12,7 @@
 #include "cache.h"
 #include "xdr3.h"
 #include "vfs.h"
+#include "trace.h"
 
 #define NFSDDBG_FACILITY		NFSDDBG_PROC
 
@@ -102,6 +103,8 @@ nfsd3_proc_lookup(struct svc_rqst *rqstp)
 				    argp->name,
 				    argp->len,
 				    &resp->fh);
+	trace_nfsd_lookup(rqstp, &resp->dirfh, argp->name, argp->len,
+			  &resp->fh, nfserr);
 	RETURN_STATUS(nfserr);
 }
 
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index da94d769ed0a..379077254f9a 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -772,6 +772,7 @@ nfsd4_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	status = nfsd_lookup(rqstp, &cstate->current_fh,
 			     u->lookup.lo_name, u->lookup.lo_len,
 			     &cstate->current_fh);
+	trace_nfsd4_lookup(rqstp, u->lookup.lo_name, u->lookup.lo_len, status);
 	trace_nfsd4_fh_current(rqstp, &cstate->current_fh);
 	return status;
 }
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 6e0b066480c5..c822ee2ef8ae 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "xdr.h"
 #include "vfs.h"
+#include "trace.h"
 
 typedef struct svc_rqst	svc_rqst;
 typedef struct svc_buf	svc_buf;
@@ -137,6 +138,8 @@ nfsd_proc_lookup(struct svc_rqst *rqstp)
 	fh_init(&resp->fh, NFS_FHSIZE);
 	nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
 				 &resp->fh);
+	trace_nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
+			  &resp->fh, nfserr);
 
 	fh_put(&argp->fh);
 	return nfsd_return_dirop(nfserr, resp);
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 2e4697324aa8..fd212fc1fd2a 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -1105,6 +1105,62 @@ TRACE_EVENT(nfsd4_fh_current,
 		__entry->xid, __entry->fh_hash, __get_str(name))
 );
 
+TRACE_EVENT(nfsd_lookup,
+	TP_PROTO(
+		const struct svc_rqst *rqstp,
+		const struct svc_fh *parent,
+		const char *name,
+		unsigned int namelen,
+		const struct svc_fh *result,
+		__be32 status
+	),
+	TP_ARGS(rqstp, parent, name, namelen, result, status),
+	TP_STRUCT__entry(
+		__field(u32, xid)
+		__field(u32, parent)
+		__field(u32, result)
+		__field(int, status)
+		__dynamic_array(unsigned char, name, namelen + 1)
+	),
+	TP_fast_assign(
+		__entry->xid = be32_to_cpu(rqstp->rq_xid);
+		__entry->parent = knfsd_fh_hash(&parent->fh_handle);
+		__entry->result = (status == nfs_ok) ?
+					knfsd_fh_hash(&result->fh_handle) : 0;
+		__entry->status = be32_to_cpu(status);
+		memcpy(__get_str(name), name, namelen);
+		__get_str(name)[namelen] = '\0';
+	),
+	TP_printk("xid=0x%08x parent fh_hash=0x%08x name=%s result fh_hash=0x%08x status=%d",
+		__entry->xid, __entry->parent, __get_str(name),
+		__entry->result, __entry->status
+	)
+);
+
+TRACE_EVENT(nfsd4_lookup,
+	TP_PROTO(
+		const struct svc_rqst *rqstp,
+		const char *name,
+		unsigned int namelen,
+		__be32 status
+	),
+	TP_ARGS(rqstp, name, namelen, status),
+	TP_STRUCT__entry(
+		__field(u32, xid)
+		__field(int, status)
+		__dynamic_array(unsigned char, name, namelen + 1)
+	),
+	TP_fast_assign(
+		__entry->xid = be32_to_cpu(rqstp->rq_xid);
+		__entry->status = be32_to_cpu(status);
+		memcpy(__get_str(name), name, namelen);
+		__get_str(name)[namelen] = '\0';
+	),
+	TP_printk("xid=0x%08x name=%s status=%d",
+		__entry->xid, __get_str(name), __entry->status
+	)
+);
+
 /*
  * from include/linux/nfs4.h
  */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 0d354531ed19..20853ab87908 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -106,6 +106,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
 	}
 	path_put(&path);
 	exp_put(exp2);
+	if (err != nfs_ok)
+		trace_printk("xid=0x%08x status=%d",
+			be32_to_cpu(rqstp->rq_xid), be32_to_cpu(err));
 out:
 	return err;
 }
@@ -179,8 +182,6 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	struct dentry		*dentry;
 	int			host_err;
 
-	dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name);
-
 	dparent = fhp->fh_dentry;
 	exp = exp_get(fhp->fh_export);
 
@@ -234,14 +235,23 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	return nfserrno(host_err);
 }
 
-/*
- * Look up one component of a pathname.
+/**
+ * nfsd_lookup - Look up one component of a pathname
+ * @rqstp: RPC Call being processed
+ * @fhp: file handle of parent directory
+ * @name: non-terminated C string of file name to look up
+ * @len: length of @name
+ * @resfh: on success, file handle of the looked-up object
+ *
  * N.B. After this call _both_ fhp and resfh need an fh_put
+ * When callers pass the same memory for @fhp and @resfh,
+ * fh_compose() fh_put's @fhp before overwritting it.
  *
  * If the lookup would cross a mountpoint, and the mounted filesystem
  * is exported to the client with NFSEXP_NOHIDE, then the lookup is
  * accepted as it stands and the mounted directory is
  * returned. Otherwise the covered directory is returned.
+ *
  * NOTE: this mountpoint crossing is not supported properly by all
  *   clients and is explicitly disallowed for NFSv3
  *      NeilBrown <neilb@cse.unsw.edu.au>
