kref_init(&dreq->kref);
        init_waitqueue_head(&dreq->wait);
        INIT_LIST_HEAD(&dreq->list);
+       dreq->iocb = NULL;
        atomic_set(&dreq->count, 0);
        atomic_set(&dreq->error, 0);
 
  * We must hold a reference to all the pages in this direct read request
  * until the RPCs complete.  This could be long *after* we are woken up in
  * nfs_direct_read_wait (for instance, if someone hits ^C on a slow server).
+ *
+ * In addition, synchronous I/O uses a stack-allocated iocb.  Thus we
+ * can't trust the iocb is still valid here if this is a synchronous
+ * request.  If the waiter is woken prematurely, the iocb is long gone.
  */
 static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
 {
 
        if (unlikely(atomic_dec_and_test(&dreq->complete))) {
                nfs_free_user_pages(dreq->pages, dreq->npages, 1);
-               wake_up(&dreq->wait);
+               if (dreq->iocb) {
+                       long res = atomic_read(&dreq->error);
+                       if (!res)
+                               res = atomic_read(&dreq->count);
+                       aio_complete(dreq->iocb, res, 0);
+               } else
+                       wake_up(&dreq->wait);
                kref_put(&dreq->kref, nfs_direct_req_release);
        }
 }
  */
 static ssize_t nfs_direct_read_wait(struct nfs_direct_req *dreq, int intr)
 {
-       int result = 0;
+       int result = -EIOCBQUEUED;
+
+       /* Async requests don't wait here */
+       if (dreq->iocb)
+               goto out;
 
+       result = 0;
        if (intr) {
                result = wait_event_interruptible(dreq->wait,
                                        (atomic_read(&dreq->complete) == 0));
        if (!result)
                result = atomic_read(&dreq->count);
 
+out:
        kref_put(&dreq->kref, nfs_direct_req_release);
        return (ssize_t) result;
 }
        dreq->npages = nr_pages;
        dreq->inode = inode;
        dreq->filp = iocb->ki_filp;
+       if (!is_sync_kiocb(iocb))
+               dreq->iocb = iocb;
 
        nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
        rpc_clnt_sigmask(clnt, &oldset);
                file->f_dentry->d_name.name,
                (unsigned long) count, (long long) pos);
 
-       if (!is_sync_kiocb(iocb))
-               goto out;
        if (count < 0)
                goto out;
        retval = -EFAULT;