Merge tag 'for-linus-5.2-ofs1' of git://git.kernel.org/pub/scm/linux/kernel/git/hubca...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 May 2019 16:37:25 +0000 (09:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 May 2019 16:37:25 +0000 (09:37 -0700)
Pull orangefs updates from Mike Marshall:
 "This includes one fix and our "Orangefs through the pagecache" patch
  series which greatly improves our small IO performance and helps us
  pass more xfstests than before.

  Fix:
   - orangefs: truncate before updating size

  Pagecache series:
   - all the rest"

* tag 'for-linus-5.2-ofs1' of git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux: (23 commits)
  orangefs: truncate before updating size
  orangefs: copy Orangefs-sized blocks into the pagecache if possible.
  orangefs: pass slot index back to readpage.
  orangefs: remember count when reading.
  orangefs: add orangefs_revalidate_mapping
  orangefs: implement writepages
  orangefs: write range tracking
  orangefs: avoid fsync service operation on flush
  orangefs: skip inode writeout if nothing to write
  orangefs: move do_readv_writev to direct_IO
  orangefs: do not return successful read when the client-core disappeared
  orangefs: implement writepage
  orangefs: migrate to generic_file_read_iter
  orangefs: service ops done for writeback are not killable
  orangefs: remove orangefs_readpages
  orangefs: reorganize setattr functions to track attribute changes
  orangefs: let setattr write to cached inode
  orangefs: set up and use backing_dev_info
  orangefs: hold i_lock during inode_getattr
  orangefs: update attributes rather than relying on server
  ...

14 files changed:
fs/orangefs/acl.c
fs/orangefs/file.c
fs/orangefs/inode.c
fs/orangefs/namei.c
fs/orangefs/orangefs-bufmap.c
fs/orangefs/orangefs-bufmap.h
fs/orangefs/orangefs-debugfs.c
fs/orangefs/orangefs-kernel.h
fs/orangefs/orangefs-mod.c
fs/orangefs/orangefs-sysfs.c
fs/orangefs/orangefs-utils.c
fs/orangefs/super.c
fs/orangefs/waitqueue.c
fs/orangefs/xattr.c

index 72d2ff1..eced272 100644 (file)
@@ -142,7 +142,7 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
                        rc = __orangefs_set_acl(inode, acl, type);
                } else {
                        iattr.ia_valid = ATTR_MODE;
-                       rc = orangefs_inode_setattr(inode, &iattr);
+                       rc = __orangefs_setattr(inode, &iattr);
                }
 
                return rc;
@@ -185,7 +185,7 @@ int orangefs_init_acl(struct inode *inode, struct inode *dir)
                inode->i_mode = mode;
                iattr.ia_mode = mode;
                iattr.ia_valid |= ATTR_MODE;
-               orangefs_inode_setattr(inode, &iattr);
+               __orangefs_setattr(inode, &iattr);
        }
 
        return error;
index b094d3d..a35c170 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * (C) 2001 Clemson University and The University of Chicago
+ * Copyright 2018 Omnibond Systems, L.L.C.
  *
  * See COPYING in top-level directory.
  */
@@ -44,15 +45,16 @@ static int flush_racache(struct inode *inode)
 /*
  * Post and wait for the I/O upcall to finish
  */
-static ssize_t wait_for_direct_io(enum ORANGEFS_io_type type, struct inode *inode,
-               loff_t *offset, struct iov_iter *iter,
-               size_t total_size, loff_t readahead_size)
+ssize_t wait_for_direct_io(enum ORANGEFS_io_type type, struct inode *inode,
+    loff_t *offset, struct iov_iter *iter, size_t total_size,
+    loff_t readahead_size, struct orangefs_write_range *wr, int *index_return)
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
        struct orangefs_kernel_op_s *new_op = NULL;
        int buffer_index = -1;
        ssize_t ret;
+       size_t copy_amount;
 
        new_op = op_alloc(ORANGEFS_VFS_OP_FILE_IO);
        if (!new_op)
@@ -84,6 +86,10 @@ populate_shared_memory:
        new_op->upcall.req.io.buf_index = buffer_index;
        new_op->upcall.req.io.count = total_size;
        new_op->upcall.req.io.offset = *offset;
+       if (type == ORANGEFS_IO_WRITE && wr) {
+               new_op->upcall.uid = from_kuid(&init_user_ns, wr->uid);
+               new_op->upcall.gid = from_kgid(&init_user_ns, wr->gid);
+       }
 
        gossip_debug(GOSSIP_FILE_DEBUG,
                     "%s(%pU): offset: %llu total_size: %zd\n",
@@ -168,7 +174,10 @@ populate_shared_memory:
                         * trigger the write.
                         */
                        case OP_VFS_STATE_INPROGR:
-                               ret = total_size;
+                               if (type == ORANGEFS_IO_READ)
+                                       ret = -EINTR;
+                               else
+                                       ret = total_size;
                                break;
                        default:
                                gossip_err("%s: unexpected op state :%d:.\n",
@@ -204,8 +213,25 @@ populate_shared_memory:
                 *       can futher be kernel-space or user-space addresses.
                 *       or it can pointers to struct page's
                 */
+
+               /*
+                * When reading, readahead_size will only be zero when
+                * we're doing O_DIRECT, otherwise we got here from
+                * orangefs_readpage.
+                *
+                * If we got here from orangefs_readpage we want to
+                * copy either a page or the whole file into the io
+                * vector, whichever is smaller.
+                */
+               if (readahead_size)
+                       copy_amount =
+                               min(new_op->downcall.resp.io.amt_complete,
+                                       (__s64)PAGE_SIZE);
+               else
+                       copy_amount = new_op->downcall.resp.io.amt_complete;
+
                ret = orangefs_bufmap_copy_to_iovec(iter, buffer_index,
-                   new_op->downcall.resp.io.amt_complete);
+                       copy_amount);
                if (ret < 0) {
                        gossip_err("%s: Failed to copy-out buffers. Please make sure that the pvfs2-client is running (%ld)\n",
                            __func__, (long)ret);
@@ -223,246 +249,112 @@ populate_shared_memory:
 
 out:
        if (buffer_index >= 0) {
-               orangefs_bufmap_put(buffer_index);
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): PUT buffer_index %d\n",
-                            __func__, handle, buffer_index);
+               if ((readahead_size) && (type == ORANGEFS_IO_READ)) {
+                       /* readpage */
+                       *index_return = buffer_index;
+                       gossip_debug(GOSSIP_FILE_DEBUG,
+                               "%s: hold on to buffer_index :%d:\n",
+                               __func__, buffer_index);
+               } else {
+                       /* O_DIRECT */
+                       orangefs_bufmap_put(buffer_index);
+                       gossip_debug(GOSSIP_FILE_DEBUG,
+                               "%s(%pU): PUT buffer_index %d\n",
+                               __func__, handle, buffer_index);
+               }
                buffer_index = -1;
        }
        op_release(new_op);
        return ret;
 }
 
-/*
- * Common entry point for read/write/readv/writev
- * This function will dispatch it to either the direct I/O
- * or buffered I/O path depending on the mount options and/or
- * augmented/extended metadata attached to the file.
- * Note: File extended attributes override any mount options.
- */
-static ssize_t do_readv_writev(enum ORANGEFS_io_type type, struct file *file,
-               loff_t *offset, struct iov_iter *iter)
+int orangefs_revalidate_mapping(struct inode *inode)
 {
-       struct inode *inode = file->f_mapping->host;
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
-       struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
-       size_t count = iov_iter_count(iter);
-       ssize_t total_count = 0;
-       ssize_t ret = -EINVAL;
-
-       gossip_debug(GOSSIP_FILE_DEBUG,
-               "%s-BEGIN(%pU): count(%d) after estimate_max_iovecs.\n",
-               __func__,
-               handle,
-               (int)count);
-
-       if (type == ORANGEFS_IO_WRITE) {
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): proceeding with offset : %llu, "
-                            "size %d\n",
-                            __func__,
-                            handle,
-                            llu(*offset),
-                            (int)count);
-       }
+       struct address_space *mapping = inode->i_mapping;
+       unsigned long *bitlock = &orangefs_inode->bitlock;
+       int ret;
 
-       if (count == 0) {
-               ret = 0;
-               goto out;
+       while (1) {
+               ret = wait_on_bit(bitlock, 1, TASK_KILLABLE);
+               if (ret)
+                       return ret;
+               spin_lock(&inode->i_lock);
+               if (test_bit(1, bitlock)) {
+                       spin_unlock(&inode->i_lock);
+                       continue;
+               }
+               if (!time_before(jiffies, orangefs_inode->mapping_time))
+                       break;
+               spin_unlock(&inode->i_lock);
+               return 0;
        }
 
-       while (iov_iter_count(iter)) {
-               size_t each_count = iov_iter_count(iter);
-               size_t amt_complete;
-
-               /* how much to transfer in this loop iteration */
-               if (each_count > orangefs_bufmap_size_query())
-                       each_count = orangefs_bufmap_size_query();
+       set_bit(1, bitlock);
+       smp_wmb();
+       spin_unlock(&inode->i_lock);
 
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): size of each_count(%d)\n",
-                            __func__,
-                            handle,
-                            (int)each_count);
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): BEFORE wait_for_io: offset is %d\n",
-                            __func__,
-                            handle,
-                            (int)*offset);
-
-               ret = wait_for_direct_io(type, inode, offset, iter,
-                               each_count, 0);
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): return from wait_for_io:%d\n",
-                            __func__,
-                            handle,
-                            (int)ret);
+       unmap_mapping_range(mapping, 0, 0, 0);
+       ret = filemap_write_and_wait(mapping);
+       if (!ret)
+               ret = invalidate_inode_pages2(mapping);
 
-               if (ret < 0)
-                       goto out;
-
-               *offset += ret;
-               total_count += ret;
-               amt_complete = ret;
+       orangefs_inode->mapping_time = jiffies +
+           orangefs_cache_timeout_msecs*HZ/1000;
 
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s(%pU): AFTER wait_for_io: offset is %d\n",
-                            __func__,
-                            handle,
-                            (int)*offset);
-
-               /*
-                * if we got a short I/O operations,
-                * fall out and return what we got so far
-                */
-               if (amt_complete < each_count)
-                       break;
-       } /*end while */
-
-out:
-       if (total_count > 0)
-               ret = total_count;
-       if (ret > 0) {
-               if (type == ORANGEFS_IO_READ) {
-                       file_accessed(file);
-               } else {
-                       file_update_time(file);
-                       /*
-                        * Must invalidate to ensure write loop doesn't
-                        * prevent kernel from reading updated
-                        * attribute.  Size probably changed because of
-                        * the write, and other clients could update
-                        * any other attribute.
-                        */
-                       orangefs_inode->getattr_time = jiffies - 1;
-               }
-       }
-
-       gossip_debug(GOSSIP_FILE_DEBUG,
-                    "%s(%pU): Value(%d) returned.\n",
-                    __func__,
-                    handle,
-                    (int)ret);
+       clear_bit(1, bitlock);
+       smp_mb__after_atomic();
+       wake_up_bit(bitlock, 1);
 
        return ret;
 }
 
-/*
- * Read data from a specified offset in a file (referenced by inode).
- * Data may be placed either in a user or kernel buffer.
- */
-ssize_t orangefs_inode_read(struct inode *inode,
-                           struct iov_iter *iter,
-                           loff_t *offset,
-                           loff_t readahead_size)
+static ssize_t orangefs_file_read_iter(struct kiocb *iocb,
+    struct iov_iter *iter)
 {
-       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
-       size_t count = iov_iter_count(iter);
-       size_t bufmap_size;
-       ssize_t ret = -EINVAL;
+       int ret;
+       struct orangefs_read_options *ro;
 
        orangefs_stats.reads++;
 
-       bufmap_size = orangefs_bufmap_size_query();
-       if (count > bufmap_size) {
-               gossip_debug(GOSSIP_FILE_DEBUG,
-                            "%s: count is too large (%zd/%zd)!\n",
-                            __func__, count, bufmap_size);
-               return -EINVAL;
+       /*
+        * Remember how they set "count" in read(2) or pread(2) or whatever -
+        * users can use count as a knob to control orangefs io size and later
+        * we can try to help them fill as many pages as possible in readpage.
+        */
+       if (!iocb->ki_filp->private_data) {
+               iocb->ki_filp->private_data = kmalloc(sizeof *ro, GFP_KERNEL);
+               if (!iocb->ki_filp->private_data)
+                       return(ENOMEM);
+               ro = iocb->ki_filp->private_data;
+               ro->blksiz = iter->count;
        }
 
-       gossip_debug(GOSSIP_FILE_DEBUG,
-                    "%s(%pU) %zd@%llu\n",
-                    __func__,
-                    &orangefs_inode->refn.khandle,
-                    count,
-                    llu(*offset));
-
-       ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, offset, iter,
-                       count, readahead_size);
-       if (ret > 0)
-               *offset += ret;
-
-       gossip_debug(GOSSIP_FILE_DEBUG,
-                    "%s(%pU): Value(%zd) returned.\n",
-                    __func__,
-                    &orangefs_inode->refn.khandle,
-                    ret);
+       down_read(&file_inode(iocb->ki_filp)->i_rwsem);
+       ret = orangefs_revalidate_mapping(file_inode(iocb->ki_filp));
+       if (ret)
+               goto out;
 
+       ret = generic_file_read_iter(iocb, iter);
+out:
+       up_read(&file_inode(iocb->ki_filp)->i_rwsem);
        return ret;
 }
 
-static ssize_t orangefs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+static ssize_t orangefs_file_write_iter(struct kiocb *iocb,
+    struct iov_iter *iter)
 {
-       struct file *file = iocb->ki_filp;
-       loff_t pos = iocb->ki_pos;
-       ssize_t rc = 0;
-
-       gossip_debug(GOSSIP_FILE_DEBUG, "orangefs_file_read_iter\n");
-
-       orangefs_stats.reads++;
-
-       rc = do_readv_writev(ORANGEFS_IO_READ, file, &pos, iter);
-       iocb->ki_pos = pos;
-
-       return rc;
-}
-
-static ssize_t orangefs_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
-{
-       struct file *file = iocb->ki_filp;
-       loff_t pos;
-       ssize_t rc;
-
-       gossip_debug(GOSSIP_FILE_DEBUG, "orangefs_file_write_iter\n");
-
-       inode_lock(file->f_mapping->host);
-
-       /* Make sure generic_write_checks sees an up to date inode size. */
-       if (file->f_flags & O_APPEND) {
-               rc = orangefs_inode_getattr(file->f_mapping->host, 0, 1,
-                   STATX_SIZE);
-               if (rc == -ESTALE)
-                       rc = -EIO;
-               if (rc) {
-                       gossip_err("%s: orangefs_inode_getattr failed, "
-                           "rc:%zd:.\n", __func__, rc);
-                       goto out;
-               }
-       }
-
-       rc = generic_write_checks(iocb, iter);
-
-       if (rc <= 0) {
-               gossip_err("%s: generic_write_checks failed, rc:%zd:.\n",
-                          __func__, rc);
-               goto out;
-       }
-
-       /*
-        * if we are appending, generic_write_checks would have updated
-        * pos to the end of the file, so we will wait till now to set
-        * pos...
-        */
-       pos = iocb->ki_pos;
-
-       rc = do_readv_writev(ORANGEFS_IO_WRITE,
-                            file,
-                            &pos,
-                            iter);
-       if (rc < 0) {
-               gossip_err("%s: do_readv_writev failed, rc:%zd:.\n",
-                          __func__, rc);
-               goto out;
-       }
-
-       iocb->ki_pos = pos;
+       int ret;
        orangefs_stats.writes++;
 
-out:
+       if (iocb->ki_pos > i_size_read(file_inode(iocb->ki_filp))) {
+               ret = orangefs_revalidate_mapping(file_inode(iocb->ki_filp));
+               if (ret)
+                       return ret;
+       }
 
-       inode_unlock(file->f_mapping->host);
-       return rc;
+       ret = generic_file_write_iter(iocb, iter);
+       return ret;
 }
 
 /*
@@ -528,14 +420,13 @@ static vm_fault_t orangefs_fault(struct vm_fault *vmf)
 {
        struct file *file = vmf->vma->vm_file;
        int ret;
-
-       ret = orangefs_inode_getattr(file->f_mapping->host, 0, 1,
-           STATX_SIZE);
+       ret = orangefs_inode_getattr(file->f_mapping->host,
+           ORANGEFS_GETATTR_SIZE);
        if (ret == -ESTALE)
                ret = -EIO;
        if (ret) {
-               gossip_err("%s: orangefs_inode_getattr failed, ret:%d:.\n",
-                               __func__, ret);
+               gossip_err("%s: orangefs_inode_getattr failed, "
+                   "ret:%d:.\n", __func__, ret);
                return VM_FAULT_SIGBUS;
        }
        return filemap_fault(vmf);
@@ -544,7 +435,7 @@ static vm_fault_t orangefs_fault(struct vm_fault *vmf)
 static const struct vm_operations_struct orangefs_file_vm_ops = {
        .fault = orangefs_fault,
        .map_pages = filemap_map_pages,
-       .page_mkwrite = filemap_page_mkwrite,
+       .page_mkwrite = orangefs_page_mkwrite,
 };
 
 /*
@@ -552,15 +443,18 @@ static const struct vm_operations_struct orangefs_file_vm_ops = {
  */
 static int orangefs_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
+       int ret;
+
+       ret = orangefs_revalidate_mapping(file_inode(file));
+       if (ret)
+               return ret;
+
        gossip_debug(GOSSIP_FILE_DEBUG,
                     "orangefs_file_mmap: called on %s\n",
                     (file ?
                        (char *)file->f_path.dentry->d_name.name :
                        (char *)"Unknown"));
 
-       if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
-               return -EINVAL;
-
        /* set the sequential readahead hint */
        vma->vm_flags |= VM_SEQ_READ;
        vma->vm_flags &= ~VM_RAND_READ;
@@ -600,8 +494,7 @@ static int orangefs_file_release(struct inode *inode, struct file *file)
                        gossip_debug(GOSSIP_INODE_DEBUG,
                            "flush_racache finished\n");
                }
-               truncate_inode_pages(file_inode(file)->i_mapping,
-                                    0);
+
        }
        return 0;
 }
@@ -619,6 +512,11 @@ static int orangefs_fsync(struct file *file,
                ORANGEFS_I(file_inode(file));
        struct orangefs_kernel_op_s *new_op = NULL;
 
+       ret = filemap_write_and_wait_range(file_inode(file)->i_mapping,
+           start, end);
+       if (ret < 0)
+               return ret;
+
        new_op = op_alloc(ORANGEFS_VFS_OP_FSYNC);
        if (!new_op)
                return -ENOMEM;
@@ -656,8 +554,8 @@ static loff_t orangefs_file_llseek(struct file *file, loff_t offset, int origin)
                 * NOTE: We are only interested in file size here,
                 * so we set mask accordingly.
                 */
-               ret = orangefs_inode_getattr(file->f_mapping->host, 0, 1,
-                   STATX_SIZE);
+               ret = orangefs_inode_getattr(file->f_mapping->host,
+                   ORANGEFS_GETATTR_SIZE);
                if (ret == -ESTALE)
                        ret = -EIO;
                if (ret) {
@@ -700,6 +598,42 @@ static int orangefs_lock(struct file *filp, int cmd, struct file_lock *fl)
        return rc;
 }
 
+static int orangefs_file_open(struct inode * inode, struct file *file)
+{
+       file->private_data = NULL;
+       return generic_file_open(inode, file);
+}
+
+static int orangefs_flush(struct file *file, fl_owner_t id)
+{
+       /*
+        * This is vfs_fsync_range(file, 0, LLONG_MAX, 0) without the
+        * service_operation in orangefs_fsync.
+        *
+        * Do not send fsync to OrangeFS server on a close.  Do send fsync
+        * on an explicit fsync call.  This duplicates historical OrangeFS
+        * behavior.
+        */
+       struct inode *inode = file->f_mapping->host;
+       int r;
+
+       kfree(file->private_data);
+       file->private_data = NULL;
+
+       if (inode->i_state & I_DIRTY_TIME) {
+               spin_lock(&inode->i_lock);
+               inode->i_state &= ~I_DIRTY_TIME;
+               spin_unlock(&inode->i_lock);
+               mark_inode_dirty_sync(inode);
+       }
+
+       r = filemap_write_and_wait_range(file->f_mapping, 0, LLONG_MAX);
+       if (r > 0)
+               return 0;
+       else
+               return r;
+}
+
 /** ORANGEFS implementation of VFS file operations */
 const struct file_operations orangefs_file_operations = {
        .llseek         = orangefs_file_llseek,
@@ -708,7 +642,8 @@ const struct file_operations orangefs_file_operations = {
        .lock           = orangefs_lock,
        .unlocked_ioctl = orangefs_ioctl,
        .mmap           = orangefs_file_mmap,
-       .open           = generic_file_open,
+       .open           = orangefs_file_open,
+       .flush          = orangefs_flush,
        .release        = orangefs_file_release,
        .fsync          = orangefs_fsync,
 };
index c3334ec..0c337d8 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * (C) 2001 Clemson University and The University of Chicago
+ * Copyright 2018 Omnibond Systems, L.L.C.
  *
  * See COPYING in top-level directory.
  */
 #include "orangefs-kernel.h"
 #include "orangefs-bufmap.h"
 
-static int read_one_page(struct page *page)
+static int orangefs_writepage_locked(struct page *page,
+    struct writeback_control *wbc)
 {
-       int ret;
-       int max_block;
-       ssize_t bytes_read = 0;
        struct inode *inode = page->mapping->host;
-       const __u32 blocksize = PAGE_SIZE;
-       const __u32 blockbits = PAGE_SHIFT;
-       struct iov_iter to;
-       struct bio_vec bv = {.bv_page = page, .bv_len = PAGE_SIZE};
+       struct orangefs_write_range *wr = NULL;
+       struct iov_iter iter;
+       struct bio_vec bv;
+       size_t len, wlen;
+       ssize_t ret;
+       loff_t off;
+
+       set_page_writeback(page);
+
+       len = i_size_read(inode);
+       if (PagePrivate(page)) {
+               wr = (struct orangefs_write_range *)page_private(page);
+               WARN_ON(wr->pos >= len);
+               off = wr->pos;
+               if (off + wr->len > len)
+                       wlen = len - off;
+               else
+                       wlen = wr->len;
+       } else {
+               WARN_ON(1);
+               off = page_offset(page);
+               if (off + PAGE_SIZE > len)
+                       wlen = len - off;
+               else
+                       wlen = PAGE_SIZE;
+       }
+       /* Should've been handled in orangefs_invalidatepage. */
+       WARN_ON(off == len || off + wlen > len);
+
+       bv.bv_page = page;
+       bv.bv_len = wlen;
+       bv.bv_offset = off % PAGE_SIZE;
+       WARN_ON(wlen == 0);
+       iov_iter_bvec(&iter, WRITE, &bv, 1, wlen);
+
+       ret = wait_for_direct_io(ORANGEFS_IO_WRITE, inode, &off, &iter, wlen,
+           len, wr, NULL);
+       if (ret < 0) {
+               SetPageError(page);
+               mapping_set_error(page->mapping, ret);
+       } else {
+               ret = 0;
+       }
+       if (wr) {
+               kfree(wr);
+               set_page_private(page, 0);
+               ClearPagePrivate(page);
+               put_page(page);
+       }
+       return ret;
+}
+
+static int orangefs_writepage(struct page *page, struct writeback_control *wbc)
+{
+       int ret;
+       ret = orangefs_writepage_locked(page, wbc);
+       unlock_page(page);
+       end_page_writeback(page);
+       return ret;
+}
 
-       iov_iter_bvec(&to, READ, &bv, 1, PAGE_SIZE);
+struct orangefs_writepages {
+       loff_t off;
+       size_t len;
+       kuid_t uid;
+       kgid_t gid;
+       int maxpages;
+       int npages;
+       struct page **pages;
+       struct bio_vec *bv;
+};
 
-       gossip_debug(GOSSIP_INODE_DEBUG,
-                   "orangefs_readpage called with page %p\n",
-                    page);
+static int orangefs_writepages_work(struct orangefs_writepages *ow,
+    struct writeback_control *wbc)
+{
+       struct inode *inode = ow->pages[0]->mapping->host;
+       struct orangefs_write_range *wrp, wr;
+       struct iov_iter iter;
+       ssize_t ret;
+       size_t len;
+       loff_t off;
+       int i;
+
+       len = i_size_read(inode);
+
+       for (i = 0; i < ow->npages; i++) {
+               set_page_writeback(ow->pages[i]);
+               ow->bv[i].bv_page = ow->pages[i];
+               ow->bv[i].bv_len = min(page_offset(ow->pages[i]) + PAGE_SIZE,
+                   ow->off + ow->len) -
+                   max(ow->off, page_offset(ow->pages[i]));
+               if (i == 0)
+                       ow->bv[i].bv_offset = ow->off -
+                           page_offset(ow->pages[i]);
+               else
+                       ow->bv[i].bv_offset = 0;
+       }
+       iov_iter_bvec(&iter, WRITE, ow->bv, ow->npages, ow->len);
+
+       WARN_ON(ow->off >= len);
+       if (ow->off + ow->len > len)
+               ow->len = len - ow->off;
+
+       off = ow->off;
+       wr.uid = ow->uid;
+       wr.gid = ow->gid;
+       ret = wait_for_direct_io(ORANGEFS_IO_WRITE, inode, &off, &iter, ow->len,
+           0, &wr, NULL);
+       if (ret < 0) {
+               for (i = 0; i < ow->npages; i++) {
+                       SetPageError(ow->pages[i]);
+                       mapping_set_error(ow->pages[i]->mapping, ret);
+                       if (PagePrivate(ow->pages[i])) {
+                               wrp = (struct orangefs_write_range *)
+                                   page_private(ow->pages[i]);
+                               ClearPagePrivate(ow->pages[i]);
+                               put_page(ow->pages[i]);
+                               kfree(wrp);
+                       }
+                       end_page_writeback(ow->pages[i]);
+                       unlock_page(ow->pages[i]);
+               }
+       } else {
+               ret = 0;
+               for (i = 0; i < ow->npages; i++) {
+                       if (PagePrivate(ow->pages[i])) {
+                               wrp = (struct orangefs_write_range *)
+                                   page_private(ow->pages[i]);
+                               ClearPagePrivate(ow->pages[i]);
+                               put_page(ow->pages[i]);
+                               kfree(wrp);
+                       }
+                       end_page_writeback(ow->pages[i]);
+                       unlock_page(ow->pages[i]);
+               }
+       }
+       return ret;
+}
+
+static int orangefs_writepages_callback(struct page *page,
+    struct writeback_control *wbc, void *data)
+{
+       struct orangefs_writepages *ow = data;
+       struct orangefs_write_range *wr;
+       int ret;
+
+       if (!PagePrivate(page)) {
+               unlock_page(page);
+               /* It's not private so there's nothing to write, right? */
+               printk("writepages_callback not private!\n");
+               BUG();
+               return 0;
+       }
+       wr = (struct orangefs_write_range *)page_private(page);
+
+       ret = -1;
+       if (ow->npages == 0) {
+               ow->off = wr->pos;
+               ow->len = wr->len;
+               ow->uid = wr->uid;
+               ow->gid = wr->gid;
+               ow->pages[ow->npages++] = page;
+               ret = 0;
+               goto done;
+       }
+       if (!uid_eq(ow->uid, wr->uid) || !gid_eq(ow->gid, wr->gid)) {
+               orangefs_writepages_work(ow, wbc);
+               ow->npages = 0;
+               ret = -1;
+               goto done;
+       }
+       if (ow->off + ow->len == wr->pos) {
+               ow->len += wr->len;
+               ow->pages[ow->npages++] = page;
+               ret = 0;
+               goto done;
+       }
+done:
+       if (ret == -1) {
+               if (ow->npages) {
+                       orangefs_writepages_work(ow, wbc);
+                       ow->npages = 0;
+               }
+               ret = orangefs_writepage_locked(page, wbc);
+               mapping_set_error(page->mapping, ret);
+               unlock_page(page);
+               end_page_writeback(page);
+       } else {
+               if (ow->npages == ow->maxpages) {
+                       orangefs_writepages_work(ow, wbc);
+                       ow->npages = 0;
+               }
+       }
+       return ret;
+}
+
+static int orangefs_writepages(struct address_space *mapping,
+    struct writeback_control *wbc)
+{
+       struct orangefs_writepages *ow;
+       struct blk_plug plug;
+       int ret;
+       ow = kzalloc(sizeof(struct orangefs_writepages), GFP_KERNEL);
+       if (!ow)
+               return -ENOMEM;
+       ow->maxpages = orangefs_bufmap_size_query()/PAGE_SIZE;
+       ow->pages = kcalloc(ow->maxpages, sizeof(struct page *), GFP_KERNEL);
+       if (!ow->pages) {
+               kfree(ow);
+               return -ENOMEM;
+       }
+       ow->bv = kcalloc(ow->maxpages, sizeof(struct bio_vec), GFP_KERNEL);
+       if (!ow->bv) {
+               kfree(ow->pages);
+               kfree(ow);
+               return -ENOMEM;
+       }
+       blk_start_plug(&plug);
+       ret = write_cache_pages(mapping, wbc, orangefs_writepages_callback, ow);
+       if (ow->npages)
+               ret = orangefs_writepages_work(ow, wbc);
+       blk_finish_plug(&plug);
+       kfree(ow->pages);
+       kfree(ow->bv);
+       kfree(ow);
+       return ret;
+}
 
-       max_block = ((inode->i_size / blocksize) + 1);
+static int orangefs_launder_page(struct page *);
 
-       if (page->index < max_block) {
-               loff_t blockptr_offset = (((loff_t) page->index) << blockbits);
+static int orangefs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct iov_iter iter;
+       struct bio_vec bv;
+       ssize_t ret;
+       loff_t off; /* offset into this page */
+       pgoff_t index; /* which page */
+       struct page *next_page;
+       char *kaddr;
+       struct orangefs_read_options *ro = file->private_data;
+       loff_t read_size;
+       loff_t roundedup;
+       int buffer_index = -1; /* orangefs shared memory slot */
+       int slot_index;   /* index into slot */
+       int remaining;
 
-               bytes_read = orangefs_inode_read(inode,
-                                                &to,
-                                                &blockptr_offset,
-                                                inode->i_size);
+       /*
+        * If they set some miniscule size for "count" in read(2)
+        * (for example) then let's try to read a page, or the whole file
+        * if it is smaller than a page. Once "count" goes over a page
+        * then lets round up to the highest page size multiple that is
+        * less than or equal to "count" and do that much orangefs IO and
+        * try to fill as many pages as we can from it.
+        *
+        * "count" should be represented in ro->blksiz.
+        *
+        * inode->i_size = file size.
+        */
+       if (ro) {
+               if (ro->blksiz < PAGE_SIZE) {
+                       if (inode->i_size < PAGE_SIZE)
+                               read_size = inode->i_size;
+                       else
+                               read_size = PAGE_SIZE;
+               } else {
+                       roundedup = ((PAGE_SIZE - 1) & ro->blksiz) ?
+                               ((ro->blksiz + PAGE_SIZE) & ~(PAGE_SIZE -1)) :
+                               ro->blksiz;
+                       if (roundedup > inode->i_size)
+                               read_size = inode->i_size;
+                       else
+                               read_size = roundedup;
+
+               }
+       } else {
+               read_size = PAGE_SIZE;
        }
+       if (!read_size)
+               read_size = PAGE_SIZE;
+
+       if (PageDirty(page))
+               orangefs_launder_page(page);
+
+       off = page_offset(page);
+       index = off >> PAGE_SHIFT;
+       bv.bv_page = page;
+       bv.bv_len = PAGE_SIZE;
+       bv.bv_offset = 0;
+       iov_iter_bvec(&iter, READ, &bv, 1, PAGE_SIZE);
+
+       ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, &off, &iter,
+           read_size, inode->i_size, NULL, &buffer_index);
+       remaining = ret;
        /* this will only zero remaining unread portions of the page data */
-       iov_iter_zero(~0U, &to);
+       iov_iter_zero(~0U, &iter);
        /* takes care of potential aliasing */
        flush_dcache_page(page);
-       if (bytes_read < 0) {
-               ret = bytes_read;
+       if (ret < 0) {
                SetPageError(page);
+               unlock_page(page);
+               goto out;
        } else {
                SetPageUptodate(page);
                if (PageError(page))
@@ -56,96 +329,469 @@ static int read_one_page(struct page *page)
        }
        /* unlock the page after the ->readpage() routine completes */
        unlock_page(page);
+
+       if (remaining > PAGE_SIZE) {
+               slot_index = 0;
+               while ((remaining - PAGE_SIZE) >= PAGE_SIZE) {
+                       remaining -= PAGE_SIZE;
+                       /*
+                        * It is an optimization to try and fill more than one
+                        * page... by now we've already gotten the single
+                        * page we were after, if stuff doesn't seem to
+                        * be going our way at this point just return
+                        * and hope for the best.
+                        *
+                        * If we look for pages and they're already there is
+                        * one reason to give up, and if they're not there
+                        * and we can't create them is another reason.
+                        */
+
+                       index++;
+                       slot_index++;
+                       next_page = find_get_page(inode->i_mapping, index);
+                       if (next_page) {
+                               gossip_debug(GOSSIP_FILE_DEBUG,
+                                       "%s: found next page, quitting\n",
+                                       __func__);
+                               put_page(next_page);
+                               goto out;
+                       }
+                       next_page = find_or_create_page(inode->i_mapping,
+                                                       index,
+                                                       GFP_KERNEL);
+                       /*
+                        * I've never hit this, leave it as a printk for
+                        * now so it will be obvious.
+                        */
+                       if (!next_page) {
+                               printk("%s: can't create next page, quitting\n",
+                                       __func__);
+                               goto out;
+                       }
+                       kaddr = kmap_atomic(next_page);
+                       orangefs_bufmap_page_fill(kaddr,
+                                               buffer_index,
+                                               slot_index);
+                       kunmap_atomic(kaddr);
+                       SetPageUptodate(next_page);
+                       unlock_page(next_page);
+                       put_page(next_page);
+               }
+       }
+
+out:
+       if (buffer_index != -1)
+               orangefs_bufmap_put(buffer_index);
        return ret;
 }
 
-static int orangefs_readpage(struct file *file, struct page *page)
+static int orangefs_write_begin(struct file *file,
+    struct address_space *mapping,
+    loff_t pos, unsigned len, unsigned flags, struct page **pagep,
+    void **fsdata)
 {
-       return read_one_page(page);
+       struct orangefs_write_range *wr;
+       struct page *page;
+       pgoff_t index;
+       int ret;
+
+       index = pos >> PAGE_SHIFT;
+
+       page = grab_cache_page_write_begin(mapping, index, flags);
+       if (!page)
+               return -ENOMEM;
+
+       *pagep = page;
+
+       if (PageDirty(page) && !PagePrivate(page)) {
+               /*
+                * Should be impossible.  If it happens, launder the page
+                * since we don't know what's dirty.  This will WARN in
+                * orangefs_writepage_locked.
+                */
+               ret = orangefs_launder_page(page);
+               if (ret)
+                       return ret;
+       }
+       if (PagePrivate(page)) {
+               struct orangefs_write_range *wr;
+               wr = (struct orangefs_write_range *)page_private(page);
+               if (wr->pos + wr->len == pos &&
+                   uid_eq(wr->uid, current_fsuid()) &&
+                   gid_eq(wr->gid, current_fsgid())) {
+                       wr->len += len;
+                       goto okay;
+               } else {
+                       ret = orangefs_launder_page(page);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       wr = kmalloc(sizeof *wr, GFP_KERNEL);
+       if (!wr)
+               return -ENOMEM;
+
+       wr->pos = pos;
+       wr->len = len;
+       wr->uid = current_fsuid();
+       wr->gid = current_fsgid();
+       SetPagePrivate(page);
+       set_page_private(page, (unsigned long)wr);
+       get_page(page);
+okay:
+       return 0;
 }
 
-static int orangefs_readpages(struct file *file,
-                          struct address_space *mapping,
-                          struct list_head *pages,
-                          unsigned nr_pages)
+static int orangefs_write_end(struct file *file, struct address_space *mapping,
+    loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata)
 {
-       int page_idx;
-       int ret;
+       struct inode *inode = page->mapping->host;
+       loff_t last_pos = pos + copied;
 
-       gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_readpages called\n");
-
-       for (page_idx = 0; page_idx < nr_pages; page_idx++) {
-               struct page *page;
-
-               page = lru_to_page(pages);
-               list_del(&page->lru);
-               if (!add_to_page_cache(page,
-                                      mapping,
-                                      page->index,
-                                      readahead_gfp_mask(mapping))) {
-                       ret = read_one_page(page);
-                       gossip_debug(GOSSIP_INODE_DEBUG,
-                               "failure adding page to cache, read_one_page returned: %d\n",
-                               ret);
-             } else {
-                       put_page(page);
-             }
-       }
-       BUG_ON(!list_empty(pages));
-       return 0;
+       /*
+        * No need to use i_size_read() here, the i_size
+        * cannot change under us because we hold the i_mutex.
+        */
+       if (last_pos > inode->i_size)
+               i_size_write(inode, last_pos);
+
+       /* zero the stale part of the page if we did a short copy */
+       if (!PageUptodate(page)) {
+               unsigned from = pos & (PAGE_SIZE - 1);
+               if (copied < len) {
+                       zero_user(page, from + copied, len - copied);
+               }
+               /* Set fully written pages uptodate. */
+               if (pos == page_offset(page) &&
+                   (len == PAGE_SIZE || pos + len == inode->i_size)) {
+                       zero_user_segment(page, from + copied, PAGE_SIZE);
+                       SetPageUptodate(page);
+               }
+       }
+
+       set_page_dirty(page);
+       unlock_page(page);
+       put_page(page);
+
+       mark_inode_dirty_sync(file_inode(file));
+       return copied;
 }
 
 static void orangefs_invalidatepage(struct page *page,
                                 unsigned int offset,
                                 unsigned int length)
 {
-       gossip_debug(GOSSIP_INODE_DEBUG,
-                    "orangefs_invalidatepage called on page %p "
-                    "(offset is %u)\n",
-                    page,
-                    offset);
+       struct orangefs_write_range *wr;
+       wr = (struct orangefs_write_range *)page_private(page);
+
+       if (offset == 0 && length == PAGE_SIZE) {
+               kfree((struct orangefs_write_range *)page_private(page));
+               set_page_private(page, 0);
+               ClearPagePrivate(page);
+               put_page(page);
+               return;
+       /* write range entirely within invalidate range (or equal) */
+       } else if (page_offset(page) + offset <= wr->pos &&
+           wr->pos + wr->len <= page_offset(page) + offset + length) {
+               kfree((struct orangefs_write_range *)page_private(page));
+               set_page_private(page, 0);
+               ClearPagePrivate(page);
+               put_page(page);
+               /* XXX is this right? only caller in fs */
+               cancel_dirty_page(page);
+               return;
+       /* invalidate range chops off end of write range */
+       } else if (wr->pos < page_offset(page) + offset &&
+           wr->pos + wr->len <= page_offset(page) + offset + length &&
+            page_offset(page) + offset < wr->pos + wr->len) {
+               size_t x;
+               x = wr->pos + wr->len - (page_offset(page) + offset);
+               WARN_ON(x > wr->len);
+               wr->len -= x;
+               wr->uid = current_fsuid();
+               wr->gid = current_fsgid();
+       /* invalidate range chops off beginning of write range */
+       } else if (page_offset(page) + offset <= wr->pos &&
+           page_offset(page) + offset + length < wr->pos + wr->len &&
+           wr->pos < page_offset(page) + offset + length) {
+               size_t x;
+               x = page_offset(page) + offset + length - wr->pos;
+               WARN_ON(x > wr->len);
+               wr->pos += x;
+               wr->len -= x;
+               wr->uid = current_fsuid();
+               wr->gid = current_fsgid();
+       /* invalidate range entirely within write range (punch hole) */
+       } else if (wr->pos < page_offset(page) + offset &&
+           page_offset(page) + offset + length < wr->pos + wr->len) {
+               /* XXX what do we do here... should not WARN_ON */
+               WARN_ON(1);
+               /* punch hole */
+               /*
+                * should we just ignore this and write it out anyway?
+                * it hardly makes sense
+                */
+               return;
+       /* non-overlapping ranges */
+       } else {
+               /* WARN if they do overlap */
+               if (!((page_offset(page) + offset + length <= wr->pos) ^
+                   (wr->pos + wr->len <= page_offset(page) + offset))) {
+                       WARN_ON(1);
+                       printk("invalidate range offset %llu length %u\n",
+                           page_offset(page) + offset, length);
+                       printk("write range offset %llu length %zu\n",
+                           wr->pos, wr->len);
+               }
+               return;
+       }
 
-       ClearPageUptodate(page);
-       ClearPageMappedToDisk(page);
-       return;
+       /*
+        * Above there are returns where wr is freed or where we WARN.
+        * Thus the following runs if wr was modified above.
+        */
 
+       orangefs_launder_page(page);
 }
 
 static int orangefs_releasepage(struct page *page, gfp_t foo)
 {
-       gossip_debug(GOSSIP_INODE_DEBUG,
-                    "orangefs_releasepage called on page %p\n",
-                    page);
-       return 0;
+       return !PagePrivate(page);
 }
 
-/*
- * Having a direct_IO entry point in the address_space_operations
- * struct causes the kernel to allows us to use O_DIRECT on
- * open. Nothing will ever call this thing, but in the future we
- * will need to be able to use O_DIRECT on open in order to support
- * AIO. Modeled after NFS, they do this too.
- */
+static void orangefs_freepage(struct page *page)
+{
+       if (PagePrivate(page)) {
+               kfree((struct orangefs_write_range *)page_private(page));
+               set_page_private(page, 0);
+               ClearPagePrivate(page);
+               put_page(page);
+       }
+}
+
+static int orangefs_launder_page(struct page *page)
+{
+       int r = 0;
+       struct writeback_control wbc = {
+               .sync_mode = WB_SYNC_ALL,
+               .nr_to_write = 0,
+       };
+       wait_on_page_writeback(page);
+       if (clear_page_dirty_for_io(page)) {
+               r = orangefs_writepage_locked(page, &wbc);
+               end_page_writeback(page);
+       }
+       return r;
+}
 
 static ssize_t orangefs_direct_IO(struct kiocb *iocb,
                                  struct iov_iter *iter)
 {
-       gossip_debug(GOSSIP_INODE_DEBUG,
-                    "orangefs_direct_IO: %pD\n",
-                    iocb->ki_filp);
+       /*
+        * Comment from original do_readv_writev:
+        * Common entry point for read/write/readv/writev
+        * This function will dispatch it to either the direct I/O
+        * or buffered I/O path depending on the mount options and/or
+        * augmented/extended metadata attached to the file.
+        * Note: File extended attributes override any mount options.
+        */
+       struct file *file = iocb->ki_filp;
+       loff_t pos = iocb->ki_pos;
+       enum ORANGEFS_io_type type = iov_iter_rw(iter) == WRITE ?
+            ORANGEFS_IO_WRITE : ORANGEFS_IO_READ;
+       loff_t *offset = &pos;
+       struct inode *inode = file->f_mapping->host;
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
+       size_t count = iov_iter_count(iter);
+       ssize_t total_count = 0;
+       ssize_t ret = -EINVAL;
+       int i = 0;
+
+       gossip_debug(GOSSIP_FILE_DEBUG,
+               "%s-BEGIN(%pU): count(%d) after estimate_max_iovecs.\n",
+               __func__,
+               handle,
+               (int)count);
+
+       if (type == ORANGEFS_IO_WRITE) {
+               gossip_debug(GOSSIP_FILE_DEBUG,
+                            "%s(%pU): proceeding with offset : %llu, "
+                            "size %d\n",
+                            __func__,
+                            handle,
+                            llu(*offset),
+                            (int)count);
+       }
+
+       if (count == 0) {
+               ret = 0;
+               goto out;
+       }
 
-       return -EINVAL;
+       while (iov_iter_count(iter)) {
+               size_t each_count = iov_iter_count(iter);
+               size_t amt_complete;
+               i++;
+
+               /* how much to transfer in this loop iteration */
+               if (each_count > orangefs_bufmap_size_query())
+                       each_count = orangefs_bufmap_size_query();
+
+               gossip_debug(GOSSIP_FILE_DEBUG,
+                            "%s(%pU): size of each_count(%d)\n",
+                            __func__,
+                            handle,
+                            (int)each_count);
+               gossip_debug(GOSSIP_FILE_DEBUG,
+                            "%s(%pU): BEFORE wait_for_io: offset is %d\n",
+                            __func__,
+                            handle,
+                            (int)*offset);
+
+               ret = wait_for_direct_io(type, inode, offset, iter,
+                               each_count, 0, NULL, NULL);
+               gossip_debug(GOSSIP_FILE_DEBUG,
+                            "%s(%pU): return from wait_for_io:%d\n",
+                            __func__,
+                            handle,
+                            (int)ret);
+
+               if (ret < 0)
+                       goto out;
+
+               *offset += ret;
+               total_count += ret;
+               amt_complete = ret;
+
+               gossip_debug(GOSSIP_FILE_DEBUG,
+                            "%s(%pU): AFTER wait_for_io: offset is %d\n",
+                            __func__,
+                            handle,
+                            (int)*offset);
+
+               /*
+                * if we got a short I/O operations,
+                * fall out and return what we got so far
+                */
+               if (amt_complete < each_count)
+                       break;
+       } /*end while */
+
+out:
+       if (total_count > 0)
+               ret = total_count;
+       if (ret > 0) {
+               if (type == ORANGEFS_IO_READ) {
+                       file_accessed(file);
+               } else {
+                       file_update_time(file);
+                       if (*offset > i_size_read(inode))
+                               i_size_write(inode, *offset);
+               }
+       }
+
+       gossip_debug(GOSSIP_FILE_DEBUG,
+                    "%s(%pU): Value(%d) returned.\n",
+                    __func__,
+                    handle,
+                    (int)ret);
+
+       return ret;
 }
 
 /** ORANGEFS2 implementation of address space operations */
 static const struct address_space_operations orangefs_address_operations = {
+       .writepage = orangefs_writepage,
        .readpage = orangefs_readpage,
-       .readpages = orangefs_readpages,
+       .writepages = orangefs_writepages,
+       .set_page_dirty = __set_page_dirty_nobuffers,
+       .write_begin = orangefs_write_begin,
+       .write_end = orangefs_write_end,
        .invalidatepage = orangefs_invalidatepage,
        .releasepage = orangefs_releasepage,
+       .freepage = orangefs_freepage,
+       .launder_page = orangefs_launder_page,
        .direct_IO = orangefs_direct_IO,
 };
 
+vm_fault_t orangefs_page_mkwrite(struct vm_fault *vmf)
+{
+       struct page *page = vmf->page;
+       struct inode *inode = file_inode(vmf->vma->vm_file);
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       unsigned long *bitlock = &orangefs_inode->bitlock;
+       vm_fault_t ret;
+       struct orangefs_write_range *wr;
+
+       sb_start_pagefault(inode->i_sb);
+
+       if (wait_on_bit(bitlock, 1, TASK_KILLABLE)) {
+               ret = VM_FAULT_RETRY;
+               goto out;
+       }
+
+       lock_page(page);
+       if (PageDirty(page) && !PagePrivate(page)) {
+               /*
+                * Should be impossible.  If it happens, launder the page
+                * since we don't know what's dirty.  This will WARN in
+                * orangefs_writepage_locked.
+                */
+               if (orangefs_launder_page(page)) {
+                       ret = VM_FAULT_LOCKED|VM_FAULT_RETRY;
+                       goto out;
+               }
+       }
+       if (PagePrivate(page)) {
+               wr = (struct orangefs_write_range *)page_private(page);
+               if (uid_eq(wr->uid, current_fsuid()) &&
+                   gid_eq(wr->gid, current_fsgid())) {
+                       wr->pos = page_offset(page);
+                       wr->len = PAGE_SIZE;
+                       goto okay;
+               } else {
+                       if (orangefs_launder_page(page)) {
+                               ret = VM_FAULT_LOCKED|VM_FAULT_RETRY;
+                               goto out;
+                       }
+               }
+       }
+       wr = kmalloc(sizeof *wr, GFP_KERNEL);
+       if (!wr) {
+               ret = VM_FAULT_LOCKED|VM_FAULT_RETRY;
+               goto out;
+       }
+       wr->pos = page_offset(page);
+       wr->len = PAGE_SIZE;
+       wr->uid = current_fsuid();
+       wr->gid = current_fsgid();
+       SetPagePrivate(page);
+       set_page_private(page, (unsigned long)wr);
+       get_page(page);
+okay:
+
+       file_update_time(vmf->vma->vm_file);
+       if (page->mapping != inode->i_mapping) {
+               unlock_page(page);
+               ret = VM_FAULT_LOCKED|VM_FAULT_NOPAGE;
+               goto out;
+       }
+
+       /*
+        * We mark the page dirty already here so that when freeze is in
+        * progress, we are guaranteed that writeback during freezing will
+        * see the dirty page and writeprotect it again.
+        */
+       set_page_dirty(page);
+       wait_for_stable_page(page);
+       ret = VM_FAULT_LOCKED;
+out:
+       sb_end_pagefault(inode->i_sb);
+       return ret;
+}
+
 static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr)
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
@@ -162,7 +808,7 @@ static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr)
                     iattr->ia_size);
 
        /* Ensure that we have a up to date size, so we know if it changed. */
-       ret = orangefs_inode_getattr(inode, 0, 1, STATX_SIZE);
+       ret = orangefs_inode_getattr(inode, ORANGEFS_GETATTR_SIZE);
        if (ret == -ESTALE)
                ret = -EIO;
        if (ret) {
@@ -172,7 +818,11 @@ static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr)
        }
        orig_size = i_size_read(inode);
 
-       truncate_setsize(inode, iattr->ia_size);
+       /* This is truncate_setsize in a different order. */
+       truncate_pagecache(inode, iattr->ia_size);
+       i_size_write(inode, iattr->ia_size);
+       if (iattr->ia_size > orig_size)
+               pagecache_isize_extended(inode, orig_size, iattr->ia_size);
 
        new_op = op_alloc(ORANGEFS_VFS_OP_TRUNCATE);
        if (!new_op)
@@ -202,22 +852,33 @@ static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr)
        return ret;
 }
 
-/*
- * Change attributes of an object referenced by dentry.
- */
-int orangefs_setattr(struct dentry *dentry, struct iattr *iattr)
+int __orangefs_setattr(struct inode *inode, struct iattr *iattr)
 {
-       int ret = -EINVAL;
-       struct inode *inode = dentry->d_inode;
-
-       gossip_debug(GOSSIP_INODE_DEBUG,
-               "%s: called on %pd\n",
-               __func__,
-               dentry);
+       int ret;
 
-       ret = setattr_prepare(dentry, iattr);
-       if (ret)
-               goto out;
+       if (iattr->ia_valid & ATTR_MODE) {
+               if (iattr->ia_mode & (S_ISVTX)) {
+                       if (is_root_handle(inode)) {
+                               /*
+                                * allow sticky bit to be set on root (since
+                                * it shows up that way by default anyhow),
+                                * but don't show it to the server
+                                */
+                               iattr->ia_mode -= S_ISVTX;
+                       } else {
+                               gossip_debug(GOSSIP_UTILS_DEBUG,
+                                            "User attempted to set sticky bit on non-root directory; returning EINVAL.\n");
+                               ret = -EINVAL;
+                               goto out;
+                       }
+               }
+               if (iattr->ia_mode & (S_ISUID)) {
+                       gossip_debug(GOSSIP_UTILS_DEBUG,
+                                    "Attempting to set setuid bit (not supported); returning EINVAL.\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
 
        if (iattr->ia_valid & ATTR_SIZE) {
                ret = orangefs_setattr_size(inode, iattr);
@@ -225,21 +886,51 @@ int orangefs_setattr(struct dentry *dentry, struct iattr *iattr)
                        goto out;
        }
 
+again:
+       spin_lock(&inode->i_lock);
+       if (ORANGEFS_I(inode)->attr_valid) {
+               if (uid_eq(ORANGEFS_I(inode)->attr_uid, current_fsuid()) &&
+                   gid_eq(ORANGEFS_I(inode)->attr_gid, current_fsgid())) {
+                       ORANGEFS_I(inode)->attr_valid = iattr->ia_valid;
+               } else {
+                       spin_unlock(&inode->i_lock);
+                       write_inode_now(inode, 1);
+                       goto again;
+               }
+       } else {
+               ORANGEFS_I(inode)->attr_valid = iattr->ia_valid;
+               ORANGEFS_I(inode)->attr_uid = current_fsuid();
+               ORANGEFS_I(inode)->attr_gid = current_fsgid();
+       }
        setattr_copy(inode, iattr);
+       spin_unlock(&inode->i_lock);
        mark_inode_dirty(inode);
 
-       ret = orangefs_inode_setattr(inode, iattr);
-       gossip_debug(GOSSIP_INODE_DEBUG,
-               "%s: orangefs_inode_setattr returned %d\n",
-               __func__,
-               ret);
-
-       if (!ret && (iattr->ia_valid & ATTR_MODE))
+       if (iattr->ia_valid & ATTR_MODE)
                /* change mod on a file that has ACLs */
                ret = posix_acl_chmod(inode, inode->i_mode);
 
+       ret = 0;
 out:
-       gossip_debug(GOSSIP_INODE_DEBUG, "%s: ret:%d:\n", __func__, ret);
+       return ret;
+}
+
+/*
+ * Change attributes of an object referenced by dentry.
+ */
+int orangefs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+       int ret;
+       gossip_debug(GOSSIP_INODE_DEBUG, "__orangefs_setattr: called on %pd\n",
+           dentry);
+       ret = setattr_prepare(dentry, iattr);
+       if (ret)
+               goto out;
+       ret = __orangefs_setattr(d_inode(dentry), iattr);
+       sync_inode_metadata(d_inode(dentry), 1);
+out:
+       gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_setattr: returning %d\n",
+           ret);
        return ret;
 }
 
@@ -253,10 +944,11 @@ int orangefs_getattr(const struct path *path, struct kstat *stat,
        struct inode *inode = path->dentry->d_inode;
 
        gossip_debug(GOSSIP_INODE_DEBUG,
-                    "orangefs_getattr: called on %pd\n",
-                    path->dentry);
+                    "orangefs_getattr: called on %pd mask %u\n",
+                    path->dentry, request_mask);
 
-       ret = orangefs_inode_getattr(inode, 0, 0, request_mask);
+       ret = orangefs_inode_getattr(inode,
+           request_mask & STATX_SIZE ? ORANGEFS_GETATTR_SIZE : 0);
        if (ret == 0) {
                generic_fillattr(inode, stat);
 
@@ -284,7 +976,7 @@ int orangefs_permission(struct inode *inode, int mask)
        gossip_debug(GOSSIP_INODE_DEBUG, "%s: refreshing\n", __func__);
 
        /* Make sure the permission (and other common attrs) are up to date. */
-       ret = orangefs_inode_getattr(inode, 0, 0, STATX_MODE);
+       ret = orangefs_inode_getattr(inode, 0);
        if (ret < 0)
                return ret;
 
@@ -304,7 +996,7 @@ int orangefs_update_time(struct inode *inode, struct timespec64 *time, int flags
                iattr.ia_valid |= ATTR_CTIME;
        if (flags & S_MTIME)
                iattr.ia_valid |= ATTR_MTIME;
-       return orangefs_inode_setattr(inode, &iattr);
+       return __orangefs_setattr(inode, &iattr);
 }
 
 /* ORANGEFS2 implementation of VFS inode operations for files */
@@ -364,6 +1056,10 @@ static int orangefs_set_inode(struct inode *inode, void *data)
        struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data;
        ORANGEFS_I(inode)->refn.fs_id = ref->fs_id;
        ORANGEFS_I(inode)->refn.khandle = ref->khandle;
+       ORANGEFS_I(inode)->attr_valid = 0;
+       hash_init(ORANGEFS_I(inode)->xattr_cache);
+       ORANGEFS_I(inode)->mapping_time = jiffies - 1;
+       ORANGEFS_I(inode)->bitlock = 0;
        return 0;
 }
 
@@ -409,7 +1105,7 @@ struct inode *orangefs_iget(struct super_block *sb,
        if (!(inode->i_state & I_NEW))
                return inode;
 
-       error = orangefs_inode_getattr(inode, 1, 1, STATX_ALL);
+       error = orangefs_inode_getattr(inode, ORANGEFS_GETATTR_NEW);
        if (error) {
                iget_failed(inode);
                return ERR_PTR(error);
@@ -454,17 +1150,11 @@ struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
        orangefs_set_inode(inode, ref);
        inode->i_ino = hash;    /* needed for stat etc */
 
-       error = orangefs_inode_getattr(inode, 1, 1, STATX_ALL);
+       error = orangefs_inode_getattr(inode, ORANGEFS_GETATTR_NEW);
        if (error)
                goto out_iput;
 
        orangefs_init_iops(inode);
-
-       inode->i_mode = mode;
-       inode->i_uid = current_fsuid();
-       inode->i_gid = current_fsgid();
-       inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
-       inode->i_size = PAGE_SIZE;
        inode->i_rdev = dev;
 
        error = insert_inode_locked4(inode, hash, orangefs_test_inode, ref);
index c8676c9..1dd710e 100644 (file)
@@ -76,19 +76,16 @@ static int orangefs_create(struct inode *dir,
 
        d_instantiate_new(dentry, inode);
        orangefs_set_timeout(dentry);
-       ORANGEFS_I(inode)->getattr_time = jiffies - 1;
-       ORANGEFS_I(inode)->getattr_mask = STATX_BASIC_STATS;
 
        gossip_debug(GOSSIP_NAME_DEBUG,
                     "%s: dentry instantiated for %pd\n",
                     __func__,
                     dentry);
 
-       dir->i_mtime = dir->i_ctime = current_time(dir);
        memset(&iattr, 0, sizeof iattr);
-       iattr.ia_valid |= ATTR_MTIME;
-       orangefs_inode_setattr(dir, &iattr);
-       mark_inode_dirty_sync(dir);
+       iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME;
+       iattr.ia_mtime = iattr.ia_ctime = current_time(dir);
+       __orangefs_setattr(dir, &iattr);
        ret = 0;
 out:
        op_release(new_op);
@@ -210,11 +207,10 @@ static int orangefs_unlink(struct inode *dir, struct dentry *dentry)
        if (!ret) {
                drop_nlink(inode);
 
-               dir->i_mtime = dir->i_ctime = current_time(dir);
                memset(&iattr, 0, sizeof iattr);
-               iattr.ia_valid |= ATTR_MTIME;
-               orangefs_inode_setattr(dir, &iattr);
-               mark_inode_dirty_sync(dir);
+               iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME;
+               iattr.ia_mtime = iattr.ia_ctime = current_time(dir);
+               __orangefs_setattr(dir, &iattr);
        }
        return ret;
 }
@@ -291,19 +287,16 @@ static int orangefs_symlink(struct inode *dir,
 
        d_instantiate_new(dentry, inode);
        orangefs_set_timeout(dentry);
-       ORANGEFS_I(inode)->getattr_time = jiffies - 1;
-       ORANGEFS_I(inode)->getattr_mask = STATX_BASIC_STATS;
 
        gossip_debug(GOSSIP_NAME_DEBUG,
                     "Inode (Symlink) %pU -> %pd\n",
                     get_khandle_from_ino(inode),
                     dentry);
 
-       dir->i_mtime = dir->i_ctime = current_time(dir);
        memset(&iattr, 0, sizeof iattr);
-       iattr.ia_valid |= ATTR_MTIME;
-       orangefs_inode_setattr(dir, &iattr);
-       mark_inode_dirty_sync(dir);
+       iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME;
+       iattr.ia_mtime = iattr.ia_ctime = current_time(dir);
+       __orangefs_setattr(dir, &iattr);
        ret = 0;
 out:
        op_release(new_op);
@@ -360,8 +353,6 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
 
        d_instantiate_new(dentry, inode);
        orangefs_set_timeout(dentry);
-       ORANGEFS_I(inode)->getattr_time = jiffies - 1;
-       ORANGEFS_I(inode)->getattr_mask = STATX_BASIC_STATS;
 
        gossip_debug(GOSSIP_NAME_DEBUG,
                     "Inode (Directory) %pU -> %pd\n",
@@ -372,11 +363,10 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
         * NOTE: we have no good way to keep nlink consistent for directories
         * across clients; keep constant at 1.
         */
-       dir->i_mtime = dir->i_ctime = current_time(dir);
        memset(&iattr, 0, sizeof iattr);
-       iattr.ia_valid |= ATTR_MTIME;
-       orangefs_inode_setattr(dir, &iattr);
-       mark_inode_dirty_sync(dir);
+       iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME;
+       iattr.ia_mtime = iattr.ia_ctime = current_time(dir);
+       __orangefs_setattr(dir, &iattr);
 out:
        op_release(new_op);
        return ret;
@@ -389,6 +379,7 @@ static int orangefs_rename(struct inode *old_dir,
                        unsigned int flags)
 {
        struct orangefs_kernel_op_s *new_op;
+       struct iattr iattr;
        int ret;
 
        if (flags)
@@ -398,7 +389,10 @@ static int orangefs_rename(struct inode *old_dir,
                     "orangefs_rename: called (%pd2 => %pd2) ct=%d\n",
                     old_dentry, new_dentry, d_count(new_dentry));
 
-       ORANGEFS_I(new_dentry->d_parent->d_inode)->getattr_time = jiffies - 1;
+       memset(&iattr, 0, sizeof iattr);
+       iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME;
+       iattr.ia_mtime = iattr.ia_ctime = current_time(new_dir);
+       __orangefs_setattr(new_dir, &iattr);
 
        new_op = op_alloc(ORANGEFS_VFS_OP_RENAME);
        if (!new_op)
index 443bcd8..d4811f9 100644 (file)
@@ -538,3 +538,16 @@ int orangefs_bufmap_copy_to_iovec(struct iov_iter *iter,
        }
        return 0;
 }
+
+void orangefs_bufmap_page_fill(void *page_to,
+                               int buffer_index,
+                               int slot_index)
+{
+       struct orangefs_bufmap_desc *from;
+       void *page_from;
+
+       from = &__orangefs_bufmap->desc_array[buffer_index];
+       page_from = kmap_atomic(from->page_array[slot_index]);
+       memcpy(page_to, page_from, PAGE_SIZE);
+       kunmap_atomic(page_from);
+}
index c2c3c5a..75b2d28 100644 (file)
@@ -34,4 +34,6 @@ int orangefs_bufmap_copy_to_iovec(struct iov_iter *iter,
                              int buffer_index,
                              size_t size);
 
+void orangefs_bufmap_page_fill(void *kaddr, int buffer_index, int slot_index);
+
 #endif /* __ORANGEFS_BUFMAP_H */
index 0732cb0..87b1a6f 100644 (file)
@@ -963,7 +963,7 @@ int orangefs_debugfs_new_client_mask(void __user *arg)
        return ret;
 }
 
-int orangefs_debugfs_new_client_string(void __user *arg) 
+int orangefs_debugfs_new_client_string(void __user *arg)
 {
        int ret;
 
@@ -1016,7 +1016,7 @@ int orangefs_debugfs_new_client_string(void __user *arg)
        return 0;
 }
 
-int orangefs_debugfs_new_debug(void __user *arg) 
+int orangefs_debugfs_new_debug(void __user *arg)
 {
        struct dev_mask_info_s mask_info = {0};
        int ret;
index 17b24ad..572dd29 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/rwsem.h>
 #include <linux/xattr.h>
 #include <linux/exportfs.h>
+#include <linux/hashtable.h>
 
 #include <asm/unaligned.h>
 
@@ -192,7 +193,13 @@ struct orangefs_inode_s {
        sector_t last_failed_block_index_read;
 
        unsigned long getattr_time;
-       u32 getattr_mask;
+       unsigned long mapping_time;
+       int attr_valid;
+       kuid_t attr_uid;
+       kgid_t attr_gid;
+       unsigned long bitlock;
+
+       DECLARE_HASHTABLE(xattr_cache, 4);
 };
 
 /* per superblock private orangefs info */
@@ -217,6 +224,25 @@ struct orangefs_stats {
        unsigned long writes;
 };
 
+struct orangefs_cached_xattr {
+       struct hlist_node node;
+       char key[ORANGEFS_MAX_XATTR_NAMELEN];
+       char val[ORANGEFS_MAX_XATTR_VALUELEN];
+       ssize_t length;
+       unsigned long timeout;
+};
+
+struct orangefs_write_range {
+       loff_t pos;
+       size_t len;
+       kuid_t uid;
+       kgid_t gid;
+};
+
+struct orangefs_read_options {
+       ssize_t blksiz;
+};
+
 extern struct orangefs_stats orangefs_stats;
 
 /*
@@ -329,13 +355,15 @@ void fsid_key_table_finalize(void);
 /*
  * defined in inode.c
  */
+vm_fault_t orangefs_page_mkwrite(struct vm_fault *);
 struct inode *orangefs_new_inode(struct super_block *sb,
                              struct inode *dir,
                              int mode,
                              dev_t dev,
                              struct orangefs_object_kref *ref);
 
-int orangefs_setattr(struct dentry *dentry, struct iattr *iattr);
+int __orangefs_setattr(struct inode *, struct iattr *);
+int orangefs_setattr(struct dentry *, struct iattr *);
 
 int orangefs_getattr(const struct path *path, struct kstat *stat,
                     u32 request_mask, unsigned int flags);
@@ -355,11 +383,6 @@ ssize_t orangefs_listxattr(struct dentry *dentry, char *buffer, size_t size);
 struct inode *orangefs_iget(struct super_block *sb,
                         struct orangefs_object_kref *ref);
 
-ssize_t orangefs_inode_read(struct inode *inode,
-                           struct iov_iter *iter,
-                           loff_t *offset,
-                           loff_t readahead_size);
-
 /*
  * defined in devorangefs-req.c
  */
@@ -371,6 +394,15 @@ int is_daemon_in_service(void);
 bool __is_daemon_in_service(void);
 
 /*
+ * defined in file.c
+ */
+int orangefs_revalidate_mapping(struct inode *);
+ssize_t wait_for_direct_io(enum ORANGEFS_io_type, struct inode *, loff_t *,
+    struct iov_iter *, size_t, loff_t, struct orangefs_write_range *, int *);
+ssize_t do_readv_writev(enum ORANGEFS_io_type, struct file *, loff_t *,
+    struct iov_iter *);
+
+/*
  * defined in orangefs-utils.c
  */
 __s32 fsid_of_op(struct orangefs_kernel_op_s *op);
@@ -386,12 +418,14 @@ int orangefs_inode_setxattr(struct inode *inode,
                         size_t size,
                         int flags);
 
-int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
-    u32 request_mask);
+#define ORANGEFS_GETATTR_NEW 1
+#define ORANGEFS_GETATTR_SIZE 2
+
+int orangefs_inode_getattr(struct inode *, int);
 
 int orangefs_inode_check_changed(struct inode *inode);
 
-int orangefs_inode_setattr(struct inode *inode, struct iattr *iattr);
+int orangefs_inode_setattr(struct inode *inode);
 
 bool orangefs_cancel_op_in_progress(struct orangefs_kernel_op_s *op);
 
@@ -400,6 +434,7 @@ int orangefs_normalize_to_errno(__s32 error_code);
 extern struct mutex orangefs_request_mutex;
 extern int op_timeout_secs;
 extern int slot_timeout_secs;
+extern int orangefs_cache_timeout_msecs;
 extern int orangefs_dcache_timeout_msecs;
 extern int orangefs_getattr_timeout_msecs;
 extern struct list_head orangefs_superblocks;
@@ -426,6 +461,7 @@ extern const struct dentry_operations orangefs_dentry_operations;
 #define ORANGEFS_OP_CANCELLATION  4   /* this is a cancellation */
 #define ORANGEFS_OP_NO_MUTEX      8   /* don't acquire request_mutex */
 #define ORANGEFS_OP_ASYNC         16  /* Queue it, but don't wait */
+#define ORANGEFS_OP_WRITEBACK     32
 
 int service_operation(struct orangefs_kernel_op_s *op,
                      const char *op_name,
index 85ef872..82cf8b3 100644 (file)
@@ -30,6 +30,7 @@ static ulong module_parm_debug_mask;
 __u64 orangefs_gossip_debug_mask;
 int op_timeout_secs = ORANGEFS_DEFAULT_OP_TIMEOUT_SECS;
 int slot_timeout_secs = ORANGEFS_DEFAULT_SLOT_TIMEOUT_SECS;
+int orangefs_cache_timeout_msecs = 50;
 int orangefs_dcache_timeout_msecs = 50;
 int orangefs_getattr_timeout_msecs = 50;
 
index 19739aa..3627ea9 100644 (file)
  *                     Slots are requested and waited for,
  *                     the wait times out after slot_timeout_secs.
  *
+ * What:               /sys/fs/orangefs/cache_timeout_msecs
+ * Date:               Mar 2018
+ * Contact:            Martin Brandenburg <martin@omnibond.com>
+ * Description:
+ *                     Time in milliseconds between which
+ *                     orangefs_revalidate_mapping will invalidate the page
+ *                     cache.
+ *
  * What:               /sys/fs/orangefs/dcache_timeout_msecs
  * Date:               Jul 2016
  * Contact:            Martin Brandenburg <martin@omnibond.com>
@@ -222,6 +230,13 @@ static ssize_t sysfs_int_show(struct kobject *kobj,
                                       slot_timeout_secs);
                        goto out;
                } else if (!strcmp(attr->attr.name,
+                                  "cache_timeout_msecs")) {
+                       rc = scnprintf(buf,
+                                      PAGE_SIZE,
+                                      "%d\n",
+                                      orangefs_cache_timeout_msecs);
+                       goto out;
+               } else if (!strcmp(attr->attr.name,
                                   "dcache_timeout_msecs")) {
                        rc = scnprintf(buf,
                                       PAGE_SIZE,
@@ -277,6 +292,9 @@ static ssize_t sysfs_int_store(struct kobject *kobj,
        } else if (!strcmp(attr->attr.name, "slot_timeout_secs")) {
                rc = kstrtoint(buf, 0, &slot_timeout_secs);
                goto out;
+       } else if (!strcmp(attr->attr.name, "cache_timeout_msecs")) {
+               rc = kstrtoint(buf, 0, &orangefs_cache_timeout_msecs);
+               goto out;
        } else if (!strcmp(attr->attr.name, "dcache_timeout_msecs")) {
                rc = kstrtoint(buf, 0, &orangefs_dcache_timeout_msecs);
                goto out;
@@ -818,6 +836,9 @@ static struct orangefs_attribute op_timeout_secs_attribute =
 static struct orangefs_attribute slot_timeout_secs_attribute =
        __ATTR(slot_timeout_secs, 0664, sysfs_int_show, sysfs_int_store);
 
+static struct orangefs_attribute cache_timeout_msecs_attribute =
+       __ATTR(cache_timeout_msecs, 0664, sysfs_int_show, sysfs_int_store);
+
 static struct orangefs_attribute dcache_timeout_msecs_attribute =
        __ATTR(dcache_timeout_msecs, 0664, sysfs_int_show, sysfs_int_store);
 
@@ -861,6 +882,7 @@ static struct orangefs_attribute perf_time_interval_secs_attribute =
 static struct attribute *orangefs_default_attrs[] = {
        &op_timeout_secs_attribute.attr,
        &slot_timeout_secs_attribute.attr,
+       &cache_timeout_msecs_attribute.attr,
        &dcache_timeout_msecs_attribute.attr,
        &getattr_timeout_msecs_attribute.attr,
        &readahead_count_attribute.attr,
index 804c8a2..d4b7ae7 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * (C) 2001 Clemson University and The University of Chicago
+ * Copyright 2018 Omnibond Systems, L.L.C.
  *
  * See COPYING in top-level directory.
  */
@@ -135,51 +136,37 @@ static int orangefs_inode_perms(struct ORANGEFS_sys_attr_s *attrs)
  * NOTE: in kernel land, we never use the sys_attr->link_target for
  * anything, so don't bother copying it into the sys_attr object here.
  */
-static inline int copy_attributes_from_inode(struct inode *inode,
-                                            struct ORANGEFS_sys_attr_s *attrs,
-                                            struct iattr *iattr)
+static inline void copy_attributes_from_inode(struct inode *inode,
+    struct ORANGEFS_sys_attr_s *attrs)
 {
-       umode_t tmp_mode;
-
-       if (!iattr || !inode || !attrs) {
-               gossip_err("NULL iattr (%p), inode (%p), attrs (%p) "
-                          "in copy_attributes_from_inode!\n",
-                          iattr,
-                          inode,
-                          attrs);
-               return -EINVAL;
-       }
-       /*
-        * We need to be careful to only copy the attributes out of the
-        * iattr object that we know are valid.
-        */
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        attrs->mask = 0;
-       if (iattr->ia_valid & ATTR_UID) {
-               attrs->owner = from_kuid(&init_user_ns, iattr->ia_uid);
+       if (orangefs_inode->attr_valid & ATTR_UID) {
+               attrs->owner = from_kuid(&init_user_ns, inode->i_uid);
                attrs->mask |= ORANGEFS_ATTR_SYS_UID;
                gossip_debug(GOSSIP_UTILS_DEBUG, "(UID) %d\n", attrs->owner);
        }
-       if (iattr->ia_valid & ATTR_GID) {
-               attrs->group = from_kgid(&init_user_ns, iattr->ia_gid);
+       if (orangefs_inode->attr_valid & ATTR_GID) {
+               attrs->group = from_kgid(&init_user_ns, inode->i_gid);
                attrs->mask |= ORANGEFS_ATTR_SYS_GID;
                gossip_debug(GOSSIP_UTILS_DEBUG, "(GID) %d\n", attrs->group);
        }
 
-       if (iattr->ia_valid & ATTR_ATIME) {
+       if (orangefs_inode->attr_valid & ATTR_ATIME) {
                attrs->mask |= ORANGEFS_ATTR_SYS_ATIME;
-               if (iattr->ia_valid & ATTR_ATIME_SET) {
-                       attrs->atime = (time64_t)iattr->ia_atime.tv_sec;
+               if (orangefs_inode->attr_valid & ATTR_ATIME_SET) {
+                       attrs->atime = (time64_t)inode->i_atime.tv_sec;
                        attrs->mask |= ORANGEFS_ATTR_SYS_ATIME_SET;
                }
        }
-       if (iattr->ia_valid & ATTR_MTIME) {
+       if (orangefs_inode->attr_valid & ATTR_MTIME) {
                attrs->mask |= ORANGEFS_ATTR_SYS_MTIME;
-               if (iattr->ia_valid & ATTR_MTIME_SET) {
-                       attrs->mtime = (time64_t)iattr->ia_mtime.tv_sec;
+               if (orangefs_inode->attr_valid & ATTR_MTIME_SET) {
+                       attrs->mtime = (time64_t)inode->i_mtime.tv_sec;
                        attrs->mask |= ORANGEFS_ATTR_SYS_MTIME_SET;
                }
        }
-       if (iattr->ia_valid & ATTR_CTIME)
+       if (orangefs_inode->attr_valid & ATTR_CTIME)
                attrs->mask |= ORANGEFS_ATTR_SYS_CTIME;
 
        /*
@@ -188,36 +175,10 @@ static inline int copy_attributes_from_inode(struct inode *inode,
         * worry about ATTR_SIZE
         */
 
-       if (iattr->ia_valid & ATTR_MODE) {
-               tmp_mode = iattr->ia_mode;
-               if (tmp_mode & (S_ISVTX)) {
-                       if (is_root_handle(inode)) {
-                               /*
-                                * allow sticky bit to be set on root (since
-                                * it shows up that way by default anyhow),
-                                * but don't show it to the server
-                                */
-                               tmp_mode -= S_ISVTX;
-                       } else {
-                               gossip_debug(GOSSIP_UTILS_DEBUG,
-                                       "%s: setting sticky bit not supported.\n",
-                                       __func__);
-                               return -EINVAL;
-                       }
-               }
-
-               if (tmp_mode & (S_ISUID)) {
-                       gossip_debug(GOSSIP_UTILS_DEBUG,
-                               "%s: setting setuid bit not supported.\n",
-                               __func__);
-                       return -EINVAL;
-               }
-
-               attrs->perms = ORANGEFS_util_translate_mode(tmp_mode);
+       if (orangefs_inode->attr_valid & ATTR_MODE) {
+               attrs->perms = ORANGEFS_util_translate_mode(inode->i_mode);
                attrs->mask |= ORANGEFS_ATTR_SYS_PERM;
        }
-
-       return 0;
 }
 
 static int orangefs_inode_type(enum orangefs_ds_type objtype)
@@ -272,27 +233,30 @@ static int orangefs_inode_is_stale(struct inode *inode,
        return 0;
 }
 
-int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
-    u32 request_mask)
+int orangefs_inode_getattr(struct inode *inode, int flags)
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_kernel_op_s *new_op;
        loff_t inode_size;
        int ret, type;
 
-       gossip_debug(GOSSIP_UTILS_DEBUG, "%s: called on inode %pU\n", __func__,
-           get_khandle_from_ino(inode));
+       gossip_debug(GOSSIP_UTILS_DEBUG, "%s: called on inode %pU flags %d\n",
+           __func__, get_khandle_from_ino(inode), flags);
 
-       if (!new && !bypass) {
-               /*
-                * Must have all the attributes in the mask and be within cache
-                * time.
-                */
-               if ((request_mask & orangefs_inode->getattr_mask) ==
-                   request_mask &&
-                   time_before(jiffies, orangefs_inode->getattr_time))
-                       return 0;
+again:
+       spin_lock(&inode->i_lock);
+       /* Must have all the attributes in the mask and be within cache time. */
+       if ((!flags && time_before(jiffies, orangefs_inode->getattr_time)) ||
+           orangefs_inode->attr_valid || inode->i_state & I_DIRTY_PAGES) {
+               if (orangefs_inode->attr_valid) {
+                       spin_unlock(&inode->i_lock);
+                       write_inode_now(inode, 1);
+                       goto again;
+               }
+               spin_unlock(&inode->i_lock);
+               return 0;
        }
+       spin_unlock(&inode->i_lock);
 
        new_op = op_alloc(ORANGEFS_VFS_OP_GETATTR);
        if (!new_op)
@@ -302,7 +266,7 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
         * Size is the hardest attribute to get.  The incremental cost of any
         * other attribute is essentially zero.
         */
-       if (request_mask & STATX_SIZE || new)
+       if (flags)
                new_op->upcall.req.getattr.mask = ORANGEFS_ATTR_SYS_ALL_NOHINT;
        else
                new_op->upcall.req.getattr.mask =
@@ -313,13 +277,33 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
        if (ret != 0)
                goto out;
 
-       if (!new) {
+again2:
+       spin_lock(&inode->i_lock);
+       /* Must have all the attributes in the mask and be within cache time. */
+       if ((!flags && time_before(jiffies, orangefs_inode->getattr_time)) ||
+           orangefs_inode->attr_valid || inode->i_state & I_DIRTY_PAGES) {
+               if (orangefs_inode->attr_valid) {
+                       spin_unlock(&inode->i_lock);
+                       write_inode_now(inode, 1);
+                       goto again2;
+               }
+               if (inode->i_state & I_DIRTY_PAGES) {
+                       ret = 0;
+                       goto out_unlock;
+               }
+               gossip_debug(GOSSIP_UTILS_DEBUG, "%s: in cache or dirty\n",
+                   __func__);
+               ret = 0;
+               goto out_unlock;
+       }
+
+       if (!(flags & ORANGEFS_GETATTR_NEW)) {
                ret = orangefs_inode_is_stale(inode,
                    &new_op->downcall.resp.getattr.attributes,
                    new_op->downcall.resp.getattr.link_target);
                if (ret) {
                        ret = -ESTALE;
-                       goto out;
+                       goto out_unlock;
                }
        }
 
@@ -329,30 +313,26 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
        case S_IFREG:
                inode->i_flags = orangefs_inode_flags(&new_op->
                    downcall.resp.getattr.attributes);
-               if (request_mask & STATX_SIZE || new) {
+               if (flags) {
                        inode_size = (loff_t)new_op->
                            downcall.resp.getattr.attributes.size;
                        inode->i_size = inode_size;
                        inode->i_blkbits = ffs(new_op->downcall.resp.getattr.
                            attributes.blksize);
-                       spin_lock(&inode->i_lock);
                        inode->i_bytes = inode_size;
                        inode->i_blocks =
                            (inode_size + 512 - inode_size % 512)/512;
-                       spin_unlock(&inode->i_lock);
                }
                break;
        case S_IFDIR:
-               if (request_mask & STATX_SIZE || new) {
+               if (flags) {
                        inode->i_size = PAGE_SIZE;
-                       spin_lock(&inode->i_lock);
                        inode_set_bytes(inode, inode->i_size);
-                       spin_unlock(&inode->i_lock);
                }
                set_nlink(inode, 1);
                break;
        case S_IFLNK:
-               if (new) {
+               if (flags & ORANGEFS_GETATTR_NEW) {
                        inode->i_size = (loff_t)strlen(new_op->
                            downcall.resp.getattr.link_target);
                        ret = strscpy(orangefs_inode->link_target,
@@ -360,7 +340,7 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
                            ORANGEFS_NAME_MAX);
                        if (ret == -E2BIG) {
                                ret = -EIO;
-                               goto out;
+                               goto out_unlock;
                        }
                        inode->i_link = orangefs_inode->link_target;
                }
@@ -370,7 +350,7 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
                /* XXX: ESTALE?  This is what is done if it is not new. */
                orangefs_make_bad_inode(inode);
                ret = -ESTALE;
-               goto out;
+               goto out_unlock;
        }
 
        inode->i_uid = make_kuid(&init_user_ns, new_op->
@@ -393,11 +373,9 @@ int orangefs_inode_getattr(struct inode *inode, int new, int bypass,
 
        orangefs_inode->getattr_time = jiffies +
            orangefs_getattr_timeout_msecs*HZ/1000;
-       if (request_mask & STATX_SIZE || new)
-               orangefs_inode->getattr_mask = STATX_BASIC_STATS;
-       else
-               orangefs_inode->getattr_mask = STATX_BASIC_STATS & ~STATX_SIZE;
        ret = 0;
+out_unlock:
+       spin_unlock(&inode->i_lock);
 out:
        op_release(new_op);
        return ret;
@@ -436,7 +414,7 @@ out:
  * issues a orangefs setattr request to make sure the new attribute values
  * take effect if successful.  returns 0 on success; -errno otherwise
  */
-int orangefs_inode_setattr(struct inode *inode, struct iattr *iattr)
+int orangefs_inode_setattr(struct inode *inode)
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_kernel_op_s *new_op;
@@ -446,24 +424,31 @@ int orangefs_inode_setattr(struct inode *inode, struct iattr *iattr)
        if (!new_op)
                return -ENOMEM;
 
+       spin_lock(&inode->i_lock);
+       new_op->upcall.uid = from_kuid(&init_user_ns, orangefs_inode->attr_uid);
+       new_op->upcall.gid = from_kgid(&init_user_ns, orangefs_inode->attr_gid);
        new_op->upcall.req.setattr.refn = orangefs_inode->refn;
-       ret = copy_attributes_from_inode(inode,
-                      &new_op->upcall.req.setattr.attributes,
-                      iattr);
-       if (ret >= 0) {
-               ret = service_operation(new_op, __func__,
-                               get_interruptible_flag(inode));
-
-               gossip_debug(GOSSIP_UTILS_DEBUG,
-                            "orangefs_inode_setattr: returning %d\n",
-                            ret);
+       copy_attributes_from_inode(inode,
+           &new_op->upcall.req.setattr.attributes);
+       orangefs_inode->attr_valid = 0;
+       if (!new_op->upcall.req.setattr.attributes.mask) {
+               spin_unlock(&inode->i_lock);
+               op_release(new_op);
+               return 0;
        }
+       spin_unlock(&inode->i_lock);
+
+       ret = service_operation(new_op, __func__,
+           get_interruptible_flag(inode) | ORANGEFS_OP_WRITEBACK);
+       gossip_debug(GOSSIP_UTILS_DEBUG,
+           "orangefs_inode_setattr: returning %d\n", ret);
+       if (ret)
+               orangefs_make_bad_inode(inode);
 
        op_release(new_op);
 
        if (ret == 0)
                orangefs_inode->getattr_time = jiffies - 1;
-
        return ret;
 }
 
index 3784f7e..ee5efdc 100644 (file)
@@ -10,6 +10,7 @@
 #include "orangefs-bufmap.h"
 
 #include <linux/parser.h>
+#include <linux/hashtable.h>
 
 /* a cache for orangefs-inode objects (i.e. orangefs inode private data) */
 static struct kmem_cache *orangefs_inode_cache;
@@ -126,7 +127,17 @@ static struct inode *orangefs_alloc_inode(struct super_block *sb)
 
 static void orangefs_free_inode(struct inode *inode)
 {
-       kmem_cache_free(orangefs_inode_cache, ORANGEFS_I(inode));
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       struct orangefs_cached_xattr *cx;
+       struct hlist_node *tmp;
+       int i;
+
+       hash_for_each_safe(orangefs_inode->xattr_cache, i, tmp, cx, node) {
+               hlist_del(&cx->node);
+               kfree(cx);
+       }
+
+       kmem_cache_free(orangefs_inode_cache, orangefs_inode);
 }
 
 static void orangefs_destroy_inode(struct inode *inode)
@@ -138,6 +149,13 @@ static void orangefs_destroy_inode(struct inode *inode)
                        __func__, orangefs_inode, get_khandle_from_ino(inode));
 }
 
+static int orangefs_write_inode(struct inode *inode,
+                               struct writeback_control *wbc)
+{
+       gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_write_inode\n");
+       return orangefs_inode_setattr(inode);
+}
+
 /*
  * NOTE: information filled in here is typically reflected in the
  * output of the system command 'df'
@@ -297,6 +315,7 @@ static const struct super_operations orangefs_s_ops = {
        .alloc_inode = orangefs_alloc_inode,
        .free_inode = orangefs_free_inode,
        .destroy_inode = orangefs_destroy_inode,
+       .write_inode = orangefs_write_inode,
        .drop_inode = generic_delete_inode,
        .statfs = orangefs_statfs,
        .remount_fs = orangefs_remount_fs,
@@ -394,15 +413,11 @@ static int orangefs_fill_sb(struct super_block *sb,
                struct orangefs_fs_mount_response *fs_mount,
                void *data, int silent)
 {
-       int ret = -EINVAL;
-       struct inode *root = NULL;
-       struct dentry *root_dentry = NULL;
+       int ret;
+       struct inode *root;
+       struct dentry *root_dentry;
        struct orangefs_object_kref root_object;
 
-       /* alloc and init our private orangefs sb info */
-       sb->s_fs_info = kzalloc(sizeof(struct orangefs_sb_info_s), GFP_KERNEL);
-       if (!ORANGEFS_SB(sb))
-               return -ENOMEM;
        ORANGEFS_SB(sb)->sb = sb;
 
        ORANGEFS_SB(sb)->root_khandle = fs_mount->root_khandle;
@@ -425,6 +440,10 @@ static int orangefs_fill_sb(struct super_block *sb,
        sb->s_blocksize_bits = PAGE_SHIFT;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
 
+       ret = super_setup_bdi(sb);
+       if (ret)
+               return ret;
+
        root_object.khandle = ORANGEFS_SB(sb)->root_khandle;
        root_object.fs_id = ORANGEFS_SB(sb)->fs_id;
        gossip_debug(GOSSIP_SUPER_DEBUG,
@@ -503,6 +522,13 @@ struct dentry *orangefs_mount(struct file_system_type *fst,
                goto free_op;
        }
 
+       /* alloc and init our private orangefs sb info */
+       sb->s_fs_info = kzalloc(sizeof(struct orangefs_sb_info_s), GFP_KERNEL);
+       if (!ORANGEFS_SB(sb)) {
+               d = ERR_PTR(-ENOMEM);
+               goto free_op;
+       }
+
        ret = orangefs_fill_sb(sb,
              &new_op->downcall.resp.fs_mount, data,
              flags & SB_SILENT ? 1 : 0);
index 0729d26..beafc33 100644 (file)
@@ -19,7 +19,7 @@
 
 static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op,
                long timeout,
-               bool interruptible)
+               int flags)
                        __acquires(op->lock);
 static void orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s *op)
        __releases(op->lock);
@@ -143,9 +143,7 @@ retry_servicing:
        if (!(flags & ORANGEFS_OP_NO_MUTEX))
                mutex_unlock(&orangefs_request_mutex);
 
-       ret = wait_for_matching_downcall(op, timeout,
-                                        flags & ORANGEFS_OP_INTERRUPTIBLE);
-
+       ret = wait_for_matching_downcall(op, timeout, flags);
        gossip_debug(GOSSIP_WAIT_DEBUG,
                     "%s: wait_for_matching_downcall returned %d for %p\n",
                     __func__,
@@ -319,10 +317,12 @@ static void
  */
 static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op,
                long timeout,
-               bool interruptible)
+               int flags)
                        __acquires(op->lock)
 {
        long n;
+       int writeback = flags & ORANGEFS_OP_WRITEBACK,
+           interruptible = flags & ORANGEFS_OP_INTERRUPTIBLE;
 
        /*
         * There's a "schedule_timeout" inside of these wait
@@ -330,10 +330,12 @@ static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op,
         * user process that needs something done and is being
         * manipulated by the client-core process.
         */
-       if (interruptible)
+       if (writeback)
+               n = wait_for_completion_io_timeout(&op->waitq, timeout);
+       else if (!writeback && interruptible)
                n = wait_for_completion_interruptible_timeout(&op->waitq,
-                                                             timeout);
-       else
+                                                                     timeout);
+       else /* !writeback && !interruptible but compiler complains */
                n = wait_for_completion_killable_timeout(&op->waitq, timeout);
 
        spin_lock(&op->lock);
index 03bcb87..bdc285a 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * (C) 2001 Clemson University and The University of Chicago
+ * Copyright 2018 Omnibond Systems, L.L.C.
  *
  * See COPYING in top-level directory.
  */
@@ -14,7 +15,7 @@
 #include "orangefs-bufmap.h"
 #include <linux/posix_acl_xattr.h>
 #include <linux/xattr.h>
-
+#include <linux/hashtable.h>
 
 #define SYSTEM_ORANGEFS_KEY "system.pvfs2."
 #define SYSTEM_ORANGEFS_KEY_LEN 13
@@ -50,6 +51,35 @@ static inline int convert_to_internal_xattr_flags(int setxattr_flags)
        return internal_flag;
 }
 
+static unsigned int xattr_key(const char *key)
+{
+       unsigned int i = 0;
+       while (key)
+               i += *key++;
+       return i % 16;
+}
+
+static struct orangefs_cached_xattr *find_cached_xattr(struct inode *inode,
+    const char *key)
+{
+       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
+       struct orangefs_cached_xattr *cx;
+       struct hlist_head *h;
+       struct hlist_node *tmp;
+       h = &orangefs_inode->xattr_cache[xattr_key(key)];
+       if (hlist_empty(h))
+               return NULL;
+       hlist_for_each_entry_safe(cx, tmp, h, node) {
+/*             if (!time_before(jiffies, cx->timeout)) {
+                       hlist_del(&cx->node);
+                       kfree(cx);
+                       continue;
+               }*/
+               if (!strcmp(cx->key, key))
+                       return cx;
+       }
+       return NULL;
+}
 
 /*
  * Tries to get a specified key's attributes of a given
@@ -65,6 +95,7 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_kernel_op_s *new_op = NULL;
+       struct orangefs_cached_xattr *cx;
        ssize_t ret = -ENOMEM;
        ssize_t length = 0;
        int fsuid;
@@ -93,6 +124,27 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
 
        down_read(&orangefs_inode->xattr_sem);
 
+       cx = find_cached_xattr(inode, name);
+       if (cx && time_before(jiffies, cx->timeout)) {
+               if (cx->length == -1) {
+                       ret = -ENODATA;
+                       goto out_unlock;
+               } else {
+                       if (size == 0) {
+                               ret = cx->length;
+                               goto out_unlock;
+                       }
+                       if (cx->length > size) {
+                               ret = -ERANGE;
+                               goto out_unlock;
+                       }
+                       memcpy(buffer, cx->val, cx->length);
+                       memset(buffer + cx->length, 0, size - cx->length);
+                       ret = cx->length;
+                       goto out_unlock;
+               }
+       }
+
        new_op = op_alloc(ORANGEFS_VFS_OP_GETXATTR);
        if (!new_op)
                goto out_unlock;
@@ -117,6 +169,15 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
                                     " does not exist!\n",
                                     get_khandle_from_ino(inode),
                                     (char *)new_op->upcall.req.getxattr.key);
+                       cx = kmalloc(sizeof *cx, GFP_KERNEL);
+                       if (cx) {
+                               strcpy(cx->key, name);
+                               cx->length = -1;
+                               cx->timeout = jiffies +
+                                   orangefs_getattr_timeout_msecs*HZ/1000;
+                               hash_add(orangefs_inode->xattr_cache, &cx->node,
+                                   xattr_key(cx->key));
+                       }
                }
                goto out_release_op;
        }
@@ -156,6 +217,23 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name,
 
        ret = length;
 
+       if (cx) {
+               strcpy(cx->key, name);
+               memcpy(cx->val, buffer, length);
+               cx->length = length;
+               cx->timeout = jiffies + HZ;
+       } else {
+               cx = kmalloc(sizeof *cx, GFP_KERNEL);
+               if (cx) {
+                       strcpy(cx->key, name);
+                       memcpy(cx->val, buffer, length);
+                       cx->length = length;
+                       cx->timeout = jiffies + HZ;
+                       hash_add(orangefs_inode->xattr_cache, &cx->node,
+                           xattr_key(cx->key));
+               }
+       }
+
 out_release_op:
        op_release(new_op);
 out_unlock:
@@ -168,6 +246,9 @@ static int orangefs_inode_removexattr(struct inode *inode, const char *name,
 {
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_kernel_op_s *new_op = NULL;
+       struct orangefs_cached_xattr *cx;
+       struct hlist_head *h;
+       struct hlist_node *tmp;
        int ret = -ENOMEM;
 
        if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN)
@@ -209,6 +290,16 @@ static int orangefs_inode_removexattr(struct inode *inode, const char *name,
                     "orangefs_inode_removexattr: returning %d\n", ret);
 
        op_release(new_op);
+
+       h = &orangefs_inode->xattr_cache[xattr_key(name)];
+       hlist_for_each_entry_safe(cx, tmp, h, node) {
+               if (!strcmp(cx->key, name)) {
+                       hlist_del(&cx->node);
+                       kfree(cx);
+                       break;
+               }
+       }
+
 out_unlock:
        up_write(&orangefs_inode->xattr_sem);
        return ret;
@@ -226,6 +317,9 @@ int orangefs_inode_setxattr(struct inode *inode, const char *name,
        struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct orangefs_kernel_op_s *new_op;
        int internal_flag = 0;
+       struct orangefs_cached_xattr *cx;
+       struct hlist_head *h;
+       struct hlist_node *tmp;
        int ret = -ENOMEM;
 
        gossip_debug(GOSSIP_XATTR_DEBUG,
@@ -287,6 +381,16 @@ int orangefs_inode_setxattr(struct inode *inode, const char *name,
 
        /* when request is serviced properly, free req op struct */
        op_release(new_op);
+
+       h = &orangefs_inode->xattr_cache[xattr_key(name)];
+       hlist_for_each_entry_safe(cx, tmp, h, node) {
+               if (!strcmp(cx->key, name)) {
+                       hlist_del(&cx->node);
+                       kfree(cx);
+                       break;
+               }
+       }
+
 out_unlock:
        up_write(&orangefs_inode->xattr_sem);
        return ret;