+ return retval;
+}
+
+/**
+ * nfs_file_direct_write - file direct write operation for NFS files
+ * @iocb: target I/O control block
+ * @buf: user's buffer from which to write data
+ * count: number of bytes to write
+ * pos: byte offset in file where writing starts
+ *
+ * We use this function for direct writes instead of calling
+ * generic_file_aio_write() in order to avoid taking the inode
+ * semaphore and updating the i_size. The NFS server will set
+ * the new i_size and this client must read the updated size
+ * back into its cache. We let the server do generic write
+ * parameter checking and report problems.
+ *
+ * We also avoid an unnecessary invocation of generic_osync_inode(),
+ * as it is fairly meaningless to sync the metadata of an NFS file.
+ *
+ * We eliminate local atime updates, see direct read above.
+ *
+ * We avoid unnecessary page cache invalidations for normal cached
+ * readers of this file.
+ *
+ * Note that O_APPEND is not supported for NFS direct writes, as there
+ * is no atomic O_APPEND write facility in the NFS protocol.
+ */
+ssize_t
+nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos)
+{
+ ssize_t retval = -EINVAL;
+ loff_t *ppos = &iocb->ki_pos;
+ unsigned long limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+ struct file *file = iocb->ki_filp;
+ struct nfs_open_context *ctx =
+ (struct nfs_open_context *) file->private_data;
+ struct dentry *dentry = file->f_dentry;
+ struct address_space *mapping = file->f_mapping;
+ struct inode *inode = mapping->host;
+ struct iovec iov = {
+ .iov_base = (char __user *)buf,
+ .iov_len = count,
+ };
+
+ dfprintk(VFS, "nfs: direct write(%s/%s(%ld), %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino, (unsigned long) count, (unsigned long) pos);
+
+ if (!is_sync_kiocb(iocb))
+ goto out;
+ if (count < 0)
+ goto out;
+ if (pos < 0)
+ goto out;
+ retval = -EFAULT;
+ if (!access_ok(VERIFY_READ, iov.iov_base, iov.iov_len))
+ goto out;
+ if (file->f_error) {
+ retval = file->f_error;
+ file->f_error = 0;
+ goto out;
+ }
+ retval = -EFBIG;
+ if (limit != RLIM_INFINITY) {
+ if (pos >= limit) {
+ send_sig(SIGXFSZ, current, 0);
+ goto out;
+ }
+ if (count > limit - (unsigned long) pos)
+ count = limit - (unsigned long) pos;
+ }
+ retval = 0;
+ if (!count)
+ goto out;
+
+ if (mapping->nrpages) {
+ retval = filemap_fdatawrite(mapping);
+ if (retval == 0)
+ retval = nfs_wb_all(inode);
+ if (retval == 0)
+ retval = filemap_fdatawait(mapping);
+ if (retval)
+ goto out;
+ }
+
+ retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
+ if (mapping->nrpages)
+ invalidate_inode_pages2(mapping);
+ if (retval > 0)
+ *ppos = pos + retval;
+
+out:
+ return retval;
+}
+
+int nfs_init_directcache(void)
+{
+ nfs_direct_cachep = kmem_cache_create("nfs_direct_cache",
+ sizeof(struct nfs_direct_req),
+ 0, SLAB_RECLAIM_ACCOUNT,
+ NULL, NULL);
+ if (nfs_direct_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_directcache(void)
+{
+ if (kmem_cache_destroy(nfs_direct_cachep))
+ printk(KERN_INFO "nfs_direct_cache: not all structures were freed\n");