commit 578d0198968806db50885e8282da08946a3d1a7c
Author: Chuck Lever <chuck.lever@oracle.com>
Date: Thu, 11 Feb 2010 18:47:59 +0000 (13:47 -0500)

    [PATCH] NFS: Too many GETATTR and ACCESS calls after direct I/O

    The cached read and write paths initialize fattr->time_start in their
    setup procedures.  The value of fattr->time_start is propagated to
    read_cache_jiffies by nfs_update_inode().  Subsequent calls to
    nfs_attribute_timeout() will then use a good time stamp when
    computing the attribute cache timeout, and squelch unneeded GETATTR
    calls.

    Since the direct I/O paths erroneously leave the inode's
    fattr->time_start field set to zero, read_cache_jiffies for that inode
    is set to zero after any direct read or write operation.  This
    triggers an otw GETATTR or ACCESS call to update the file's attribute
    and access caches properly, even when the NFS READ or WRITE replies
    have usable post-op attributes.

    Make sure the direct read and write setup code performs the same fattr
    initialization as the cached I/O paths to prevent unnecessary GETATTR
    calls.

    This was likely introduced by commit 0e574af1 in 2.6.15, which appears
    to add new nfs_fattr_init() call sites in the cached read and write
    paths, but not in the equivalent places in fs/nfs/direct.c.  A
    subsequent commit in the same series, 33801147, introduces the
    fattr->time_start field.

    Interestingly, the direct write reschedule path already has a call to
    nfs_fattr_init() in the right place.

    Reported-by: Quentin Barnes <qbarnes@yahoo-inc.com>
    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
    Cc: stable@kernel.org

    Applied without changes to kernel-2.6.18-194.0.0.0.4.el5.

    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

diff -Naurp linux-2.6.18.x86_64/fs/nfs/direct.c cel-2.6.18/fs/nfs/direct.c
--- linux-2.6.18.x86_64/fs/nfs/direct.c	2010-05-03 10:49:12.000000000 -0400
+++ cel-2.6.18/fs/nfs/direct.c	2010-05-03 11:04:05.000000000 -0400
@@ -312,6 +312,7 @@ static ssize_t nfs_direct_read_schedule(
 		data->args.pgbase = pgbase;
 		data->args.pages = data->pagevec;
 		data->args.count = bytes;
+		nfs_fattr_init(&data->fattr);
 		data->res.fattr = &data->fattr;
 		data->res.eof = 0;
 		data->res.count = bytes;
@@ -480,6 +481,7 @@ static void nfs_direct_commit_schedule(s
 	data->res.count = 0;
 	data->res.fattr = &data->fattr;
 	data->res.verf = &data->verf;
+	nfs_fattr_init(&data->fattr);
 
 	rpc_init_task_wq(&data->task, NFS_CLIENT(dreq->inode), RPC_TASK_ASYNC,
 				&nfs_commit_direct_ops, data, nfsiod_workqueue);
@@ -652,6 +654,7 @@ static ssize_t nfs_direct_write_schedule
 		data->args.pgbase = pgbase;
 		data->args.pages = data->pagevec;
 		data->args.count = bytes;
+		nfs_fattr_init(&data->fattr);
 		data->res.fattr = &data->fattr;
 		data->res.count = bytes;
 		data->res.verf = &data->verf;
