ALSA: rme32: Fix the missing snd_card_free() call at probe error
[linux-2.6-microblaze.git] / mm / shmem.c
index 28d6274..a09b29e 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/uio.h>
 #include <linux/khugepaged.h>
 #include <linux/hugetlb.h>
-#include <linux/frontswap.h>
 #include <linux/fs_parser.h>
 #include <linux/swapfile.h>
 
@@ -554,7 +553,7 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
        struct shmem_inode_info *info;
        struct page *page;
        unsigned long batch = sc ? sc->nr_to_scan : 128;
-       int removed = 0, split = 0;
+       int split = 0;
 
        if (list_empty(&sbinfo->shrinklist))
                return SHRINK_STOP;
@@ -569,7 +568,6 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
                /* inode is about to be evicted */
                if (!inode) {
                        list_del_init(&info->shrinklist);
-                       removed++;
                        goto next;
                }
 
@@ -577,12 +575,12 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
                if (round_up(inode->i_size, PAGE_SIZE) ==
                                round_up(inode->i_size, HPAGE_PMD_SIZE)) {
                        list_move(&info->shrinklist, &to_remove);
-                       removed++;
                        goto next;
                }
 
                list_move(&info->shrinklist, &list);
 next:
+               sbinfo->shrinklist_len--;
                if (!--batch)
                        break;
        }
@@ -602,7 +600,7 @@ next:
                inode = &info->vfs_inode;
 
                if (nr_to_split && split >= nr_to_split)
-                       goto leave;
+                       goto move_back;
 
                page = find_get_page(inode->i_mapping,
                                (inode->i_size & HPAGE_PMD_MASK) >> PAGE_SHIFT);
@@ -616,38 +614,44 @@ next:
                }
 
                /*
-                * Leave the inode on the list if we failed to lock
-                * the page at this time.
+                * Move the inode on the list back to shrinklist if we failed
+                * to lock the page at this time.
                 *
                 * Waiting for the lock may lead to deadlock in the
                 * reclaim path.
                 */
                if (!trylock_page(page)) {
                        put_page(page);
-                       goto leave;
+                       goto move_back;
                }
 
                ret = split_huge_page(page);
                unlock_page(page);
                put_page(page);
 
-               /* If split failed leave the inode on the list */
+               /* If split failed move the inode on the list back to shrinklist */
                if (ret)
-                       goto leave;
+                       goto move_back;
 
                split++;
 drop:
                list_del_init(&info->shrinklist);
-               removed++;
-leave:
+               goto put;
+move_back:
+               /*
+                * Make sure the inode is either on the global list or deleted
+                * from any local list before iput() since it could be deleted
+                * in another thread once we put the inode (then the local list
+                * is corrupted).
+                */
+               spin_lock(&sbinfo->shrinklist_lock);
+               list_move(&info->shrinklist, &sbinfo->shrinklist);
+               sbinfo->shrinklist_len++;
+               spin_unlock(&sbinfo->shrinklist_lock);
+put:
                iput(inode);
        }
 
-       spin_lock(&sbinfo->shrinklist_lock);
-       list_splice_tail(&list, &sbinfo->shrinklist);
-       sbinfo->shrinklist_len -= removed;
-       spin_unlock(&sbinfo->shrinklist_lock);
-
        return split;
 }
 
@@ -1147,7 +1151,7 @@ static void shmem_evict_inode(struct inode *inode)
 static int shmem_find_swap_entries(struct address_space *mapping,
                                   pgoff_t start, unsigned int nr_entries,
                                   struct page **entries, pgoff_t *indices,
-                                  unsigned int type, bool frontswap)
+                                  unsigned int type)
 {
        XA_STATE(xas, &mapping->i_pages, start);
        struct page *page;
@@ -1168,9 +1172,6 @@ static int shmem_find_swap_entries(struct address_space *mapping,
                entry = radix_to_swp_entry(page);
                if (swp_type(entry) != type)
                        continue;
-               if (frontswap &&
-                   !frontswap_test(swap_info[type], swp_offset(entry)))
-                       continue;
 
                indices[ret] = xas.xa_index;
                entries[ret] = page;
@@ -1223,26 +1224,20 @@ static int shmem_unuse_swap_entries(struct inode *inode, struct pagevec pvec,
 /*
  * If swap found in inode, free it and move page from swapcache to filecache.
  */
-static int shmem_unuse_inode(struct inode *inode, unsigned int type,
-                            bool frontswap, unsigned long *fs_pages_to_unuse)
+static int shmem_unuse_inode(struct inode *inode, unsigned int type)
 {
        struct address_space *mapping = inode->i_mapping;
        pgoff_t start = 0;
        struct pagevec pvec;
        pgoff_t indices[PAGEVEC_SIZE];
-       bool frontswap_partial = (frontswap && *fs_pages_to_unuse > 0);
        int ret = 0;
 
        pagevec_init(&pvec);
        do {
                unsigned int nr_entries = PAGEVEC_SIZE;
 
-               if (frontswap_partial && *fs_pages_to_unuse < PAGEVEC_SIZE)
-                       nr_entries = *fs_pages_to_unuse;
-
                pvec.nr = shmem_find_swap_entries(mapping, start, nr_entries,
-                                                 pvec.pages, indices,
-                                                 type, frontswap);
+                                                 pvec.pages, indices, type);
                if (pvec.nr == 0) {
                        ret = 0;
                        break;
@@ -1252,14 +1247,6 @@ static int shmem_unuse_inode(struct inode *inode, unsigned int type,
                if (ret < 0)
                        break;
 
-               if (frontswap_partial) {
-                       *fs_pages_to_unuse -= ret;
-                       if (*fs_pages_to_unuse == 0) {
-                               ret = FRONTSWAP_PAGES_UNUSED;
-                               break;
-                       }
-               }
-
                start = indices[pvec.nr - 1];
        } while (true);
 
@@ -1271,8 +1258,7 @@ static int shmem_unuse_inode(struct inode *inode, unsigned int type,
  * device 'type' back into memory, so the swap device can be
  * unused.
  */
-int shmem_unuse(unsigned int type, bool frontswap,
-               unsigned long *fs_pages_to_unuse)
+int shmem_unuse(unsigned int type)
 {
        struct shmem_inode_info *info, *next;
        int error = 0;
@@ -1295,8 +1281,7 @@ int shmem_unuse(unsigned int type, bool frontswap,
                atomic_inc(&info->stop_eviction);
                mutex_unlock(&shmem_swaplist_mutex);
 
-               error = shmem_unuse_inode(&info->vfs_inode, type, frontswap,
-                                         fs_pages_to_unuse);
+               error = shmem_unuse_inode(&info->vfs_inode, type);
                cond_resched();
 
                mutex_lock(&shmem_swaplist_mutex);
@@ -1541,8 +1526,7 @@ static struct page *shmem_alloc_hugepage(gfp_t gfp,
                return NULL;
 
        shmem_pseudo_vma_init(&pvma, info, hindex);
-       page = alloc_pages_vma(gfp, HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(),
-                              true);
+       page = alloc_pages_vma(gfp, HPAGE_PMD_ORDER, &pvma, 0, true);
        shmem_pseudo_vma_destroy(&pvma);
        if (page)
                prep_transhuge_page(page);
@@ -2439,6 +2423,7 @@ shmem_write_begin(struct file *file, struct address_space *mapping,
        struct inode *inode = mapping->host;
        struct shmem_inode_info *info = SHMEM_I(inode);
        pgoff_t index = pos >> PAGE_SHIFT;
+       int ret = 0;
 
        /* i_rwsem is held by caller */
        if (unlikely(info->seals & (F_SEAL_GROW |
@@ -2449,7 +2434,19 @@ shmem_write_begin(struct file *file, struct address_space *mapping,
                        return -EPERM;
        }
 
-       return shmem_getpage(inode, index, pagep, SGP_WRITE);
+       ret = shmem_getpage(inode, index, pagep, SGP_WRITE);
+
+       if (ret)
+               return ret;
+
+       if (PageHWPoison(*pagep)) {
+               unlock_page(*pagep);
+               put_page(*pagep);
+               *pagep = NULL;
+               return -EIO;
+       }
+
+       return 0;
 }
 
 static int
@@ -2536,6 +2533,12 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
                        if (sgp == SGP_CACHE)
                                set_page_dirty(page);
                        unlock_page(page);
+
+                       if (PageHWPoison(page)) {
+                               put_page(page);
+                               error = -EIO;
+                               break;
+                       }
                }
 
                /*
@@ -3075,7 +3078,8 @@ static const char *shmem_get_link(struct dentry *dentry,
                page = find_get_page(inode->i_mapping, 0);
                if (!page)
                        return ERR_PTR(-ECHILD);
-               if (!PageUptodate(page)) {
+               if (PageHWPoison(page) ||
+                   !PageUptodate(page)) {
                        put_page(page);
                        return ERR_PTR(-ECHILD);
                }
@@ -3083,6 +3087,13 @@ static const char *shmem_get_link(struct dentry *dentry,
                error = shmem_getpage(inode, 0, &page, SGP_READ);
                if (error)
                        return ERR_PTR(error);
+               if (!page)
+                       return ERR_PTR(-ECHILD);
+               if (PageHWPoison(page)) {
+                       unlock_page(page);
+                       put_page(page);
+                       return ERR_PTR(-ECHILD);
+               }
                unlock_page(page);
        }
        set_delayed_call(done, shmem_put_link, page);
@@ -3733,6 +3744,13 @@ static void shmem_destroy_inodecache(void)
        kmem_cache_destroy(shmem_inode_cachep);
 }
 
+/* Keep the page in page cache instead of truncating it */
+static int shmem_error_remove_page(struct address_space *mapping,
+                                  struct page *page)
+{
+       return 0;
+}
+
 const struct address_space_operations shmem_aops = {
        .writepage      = shmem_writepage,
        .set_page_dirty = __set_page_dirty_no_writeback,
@@ -3743,7 +3761,7 @@ const struct address_space_operations shmem_aops = {
 #ifdef CONFIG_MIGRATION
        .migratepage    = migrate_page,
 #endif
-       .error_remove_page = generic_error_remove_page,
+       .error_remove_page = shmem_error_remove_page,
 };
 EXPORT_SYMBOL(shmem_aops);
 
@@ -3977,8 +3995,7 @@ int __init shmem_init(void)
        return 0;
 }
 
-int shmem_unuse(unsigned int type, bool frontswap,
-               unsigned long *fs_pages_to_unuse)
+int shmem_unuse(unsigned int type)
 {
        return 0;
 }
@@ -4151,9 +4168,14 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
        error = shmem_getpage_gfp(inode, index, &page, SGP_CACHE,
                                  gfp, NULL, NULL, NULL);
        if (error)
-               page = ERR_PTR(error);
-       else
-               unlock_page(page);
+               return ERR_PTR(error);
+
+       unlock_page(page);
+       if (PageHWPoison(page)) {
+               put_page(page);
+               return ERR_PTR(-EIO);
+       }
+
        return page;
 #else
        /*