• Jeff Layton's avatar
    nfs: close NFSv4 COMMIT vs. CLOSE race · d2224e7a
    Jeff Layton authored
    I've been adding in more artificial delays in the NFSv4 commit and close
    codepaths to uncover races. The kernel I'm testing has the patch to
    close the race in __rpc_wait_for_completion_task that's in Trond's
    cthon2011 branch. The reproducer I've been using does this in a loop:
    
    	mkdir("DIR");
    	fd = open("DIR/FILE", O_WRONLY|O_CREAT|O_EXCL, 0644);
    	write(fd, "abcdefg", 7);
    	close(fd);
    	unlink("DIR/FILE");
    	rmdir("DIR");
    
    The above reproducer shouldn't result in any silly-renaming. However,
    when I add a "msleep(100)" just after the nfs_commit_clear_lock call in
    nfs_commit_release, I can almost always force one to occur. If I can
    force it to occur with that, then it can happen without that delay
    given the right timing.
    
    nfs_commit_inode waits for the NFS_INO_COMMIT bit to clear when called
    with FLUSH_SYNC set. nfs_commit_rpcsetup on the other hand does not wait
    for the task to complete before putting its reference to it, so the last
    reference get put in rpc_release task and gets queued to a workqueue.
    
    In this situation, the last open context reference may be put by the
    COMMIT release instead of the close() syscall. The close() syscall
    returns too quickly and the unlink runs while the d_count is still
    high since the COMMIT release hasn't put its dentry reference yet.
    
    Fix this by having rpc_commit_rpcsetup wait for the RPC call to complete
    before putting the task reference when FLUSH_SYNC is set. With this, the
    last reference is put by the process that's initiating the FLUSH_SYNC
    commit and the race is closed.
    Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
    Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
    d2224e7a
write.c 40.7 KB