These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / mm / shmem.c
index 47d536e..ea5a70c 100644 (file)
@@ -73,6 +73,8 @@ static struct vfsmount *shm_mnt;
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
+#include "internal.h"
+
 #define BLOCKS_PER_PAGE  (PAGE_CACHE_SIZE/512)
 #define VM_ACCT(size)    (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
 
@@ -542,6 +544,21 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
 }
 EXPORT_SYMBOL_GPL(shmem_truncate_range);
 
+static int shmem_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                        struct kstat *stat)
+{
+       struct inode *inode = dentry->d_inode;
+       struct shmem_inode_info *info = SHMEM_I(inode);
+
+       if (info->alloced - info->swapped != inode->i_mapping->nrpages) {
+               spin_lock(&info->lock);
+               shmem_recalc_inode(inode);
+               spin_unlock(&info->lock);
+       }
+       generic_fillattr(inode, stat);
+       return 0;
+}
+
 static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
 {
        struct inode *inode = d_inode(dentry);
@@ -569,12 +586,18 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
                        i_size_write(inode, newsize);
                        inode->i_ctime = inode->i_mtime = CURRENT_TIME;
                }
-               if (newsize < oldsize) {
+               if (newsize <= oldsize) {
                        loff_t holebegin = round_up(newsize, PAGE_SIZE);
-                       unmap_mapping_range(inode->i_mapping, holebegin, 0, 1);
-                       shmem_truncate_range(inode, newsize, (loff_t)-1);
+                       if (oldsize > holebegin)
+                               unmap_mapping_range(inode->i_mapping,
+                                                       holebegin, 0, 1);
+                       if (info->alloced)
+                               shmem_truncate_range(inode,
+                                                       newsize, (loff_t)-1);
                        /* unmap again to remove racily COWed private pages */
-                       unmap_mapping_range(inode->i_mapping, holebegin, 0, 1);
+                       if (oldsize > holebegin)
+                               unmap_mapping_range(inode->i_mapping,
+                                                       holebegin, 0, 1);
                }
        }
 
@@ -597,8 +620,7 @@ static void shmem_evict_inode(struct inode *inode)
                        list_del_init(&info->swaplist);
                        mutex_unlock(&shmem_swaplist_mutex);
                }
-       } else
-               kfree(info->symlink);
+       }
 
        simple_xattrs_free(&info->xattrs);
        WARN_ON(inode->i_blocks);
@@ -820,14 +842,14 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
                list_add_tail(&info->swaplist, &shmem_swaplist);
 
        if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
-               swap_shmem_alloc(swap);
-               shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
-
                spin_lock(&info->lock);
-               info->swapped++;
                shmem_recalc_inode(inode);
+               info->swapped++;
                spin_unlock(&info->lock);
 
+               swap_shmem_alloc(swap);
+               shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
+
                mutex_unlock(&shmem_swaplist_mutex);
                BUG_ON(page_mapped(page));
                swap_writepage(page, wbc);
@@ -1008,7 +1030,7 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp,
                 */
                oldpage = newpage;
        } else {
-               mem_cgroup_migrate(oldpage, newpage, true);
+               mem_cgroup_replace_page(oldpage, newpage);
                lru_cache_add_anon(newpage);
                *pagep = newpage;
        }
@@ -1055,7 +1077,7 @@ repeat:
        if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
            ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
                error = -EINVAL;
-               goto failed;
+               goto unlock;
        }
 
        if (page && sgp == SGP_WRITE)
@@ -1223,11 +1245,15 @@ clear:
        /* Perhaps the file has been truncated since we checked */
        if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
            ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+               if (alloced) {
+                       ClearPageDirty(page);
+                       delete_from_page_cache(page);
+                       spin_lock(&info->lock);
+                       shmem_recalc_inode(inode);
+                       spin_unlock(&info->lock);
+               }
                error = -EINVAL;
-               if (alloced)
-                       goto trunc;
-               else
-                       goto failed;
+               goto unlock;
        }
        *pagep = page;
        return 0;
@@ -1235,23 +1261,13 @@ clear:
        /*
         * Error recovery.
         */
-trunc:
-       info = SHMEM_I(inode);
-       ClearPageDirty(page);
-       delete_from_page_cache(page);
-       spin_lock(&info->lock);
-       info->alloced--;
-       inode->i_blocks -= BLOCKS_PER_PAGE;
-       spin_unlock(&info->lock);
 decused:
-       sbinfo = SHMEM_SB(inode->i_sb);
        if (sbinfo->max_blocks)
                percpu_counter_add(&sbinfo->used_blocks, -1);
 unacct:
        shmem_unacct_blocks(info->flags, 1);
 failed:
-       if (swap.val && error != -EINVAL &&
-           !shmem_confirm_swap(mapping, index, swap))
+       if (swap.val && !shmem_confirm_swap(mapping, index, swap))
                error = -EEXIST;
 unlock:
        if (page) {
@@ -2445,8 +2461,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
        info = SHMEM_I(inode);
        inode->i_size = len-1;
        if (len <= SHORT_SYMLINK_LEN) {
-               info->symlink = kmemdup(symname, len, GFP_KERNEL);
-               if (!info->symlink) {
+               inode->i_link = kmemdup(symname, len, GFP_KERNEL);
+               if (!inode->i_link) {
                        iput(inode);
                        return -ENOMEM;
                }
@@ -2474,30 +2490,23 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
        return 0;
 }
 
-static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, SHMEM_I(d_inode(dentry))->symlink);
-       return NULL;
-}
-
-static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
 {
        struct page *page = NULL;
        int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL);
-       nd_set_link(nd, error ? ERR_PTR(error) : kmap(page));
-       if (page)
-               unlock_page(page);
-       return page;
+       if (error)
+               return ERR_PTR(error);
+       unlock_page(page);
+       *cookie = page;
+       return kmap(page);
 }
 
-static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+static void shmem_put_link(struct inode *unused, void *cookie)
 {
-       if (!IS_ERR(nd_get_link(nd))) {
-               struct page *page = cookie;
-               kunmap(page);
-               mark_page_accessed(page);
-               page_cache_release(page);
-       }
+       struct page *page = cookie;
+       kunmap(page);
+       mark_page_accessed(page);
+       page_cache_release(page);
 }
 
 #ifdef CONFIG_TMPFS_XATTR
@@ -2642,7 +2651,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
 
 static const struct inode_operations shmem_short_symlink_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = shmem_follow_short_symlink,
+       .follow_link    = simple_follow_link,
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,
        .getxattr       = shmem_getxattr,
@@ -3072,6 +3081,7 @@ static struct inode *shmem_alloc_inode(struct super_block *sb)
 static void shmem_destroy_callback(struct rcu_head *head)
 {
        struct inode *inode = container_of(head, struct inode, i_rcu);
+       kfree(inode->i_link);
        kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
 }
 
@@ -3128,6 +3138,7 @@ static const struct file_operations shmem_file_operations = {
 };
 
 static const struct inode_operations shmem_inode_operations = {
+       .getattr        = shmem_getattr,
        .setattr        = shmem_setattr,
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,
@@ -3369,8 +3380,8 @@ put_path:
  * shmem_kernel_file_setup - get an unlinked file living in tmpfs which must be
  *     kernel internal.  There will be NO LSM permission checks against the
  *     underlying inode.  So users of this interface must do LSM checks at a
- *     higher layer.  The one user is the big_key implementation.  LSM checks
- *     are provided at the key level rather than the inode level.
+ *     higher layer.  The users are the big_key and shm implementations.  LSM
+ *     checks are provided at the key or shm level rather than the inode.
  * @name: name for dentry (to be seen in /proc/<pid>/maps
  * @size: size to be set for the file
  * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size