Merge tag 'drm-fixes-2022-07-29' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / mm / shmem.c
index da30c76..b7f2d4a 100644 (file)
@@ -1174,6 +1174,10 @@ static int shmem_find_swap_entries(struct address_space *mapping,
                        continue;
 
                entry = radix_to_swp_entry(folio);
+               /*
+                * swapin error entries can be found in the mapping. But they're
+                * deliberately ignored here as we've done everything we can do.
+                */
                if (swp_type(entry) != type)
                        continue;
 
@@ -1671,6 +1675,36 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp,
        return error;
 }
 
+static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
+                                        struct folio *folio, swp_entry_t swap)
+{
+       struct address_space *mapping = inode->i_mapping;
+       struct shmem_inode_info *info = SHMEM_I(inode);
+       swp_entry_t swapin_error;
+       void *old;
+
+       swapin_error = make_swapin_error_entry(&folio->page);
+       old = xa_cmpxchg_irq(&mapping->i_pages, index,
+                            swp_to_radix_entry(swap),
+                            swp_to_radix_entry(swapin_error), 0);
+       if (old != swp_to_radix_entry(swap))
+               return;
+
+       folio_wait_writeback(folio);
+       delete_from_swap_cache(&folio->page);
+       spin_lock_irq(&info->lock);
+       /*
+        * Don't treat swapin error folio as alloced. Otherwise inode->i_blocks won't
+        * be 0 when inode is released and thus trigger WARN_ON(inode->i_blocks) in
+        * shmem_evict_inode.
+        */
+       info->alloced--;
+       info->swapped--;
+       shmem_recalc_inode(inode);
+       spin_unlock_irq(&info->lock);
+       swap_free(swap);
+}
+
 /*
  * Swap in the page pointed to by *pagep.
  * Caller has to make sure that *pagep contains a valid swapped page.
@@ -1694,6 +1728,9 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
        swap = radix_to_swp_entry(*foliop);
        *foliop = NULL;
 
+       if (is_swapin_error_entry(swap))
+               return -EIO;
+
        /* Look it up and read it in.. */
        page = lookup_swap_cache(swap, NULL, 0);
        if (!page) {
@@ -1761,6 +1798,8 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
 failed:
        if (!shmem_confirm_swap(mapping, index, swap))
                error = -EEXIST;
+       if (error == -EIO)
+               shmem_set_folio_swapin_error(inode, index, folio, swap);
 unlock:
        if (folio) {
                folio_unlock(folio);
@@ -1906,7 +1945,7 @@ alloc_nohuge:
 
        spin_lock_irq(&info->lock);
        info->alloced += folio_nr_pages(folio);
-       inode->i_blocks += BLOCKS_PER_PAGE << folio_order(folio);
+       inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio);
        shmem_recalc_inode(inode);
        spin_unlock_irq(&info->lock);
        alloced = true;
@@ -3353,7 +3392,7 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
                break;
        case Opt_nr_blocks:
                ctx->blocks = memparse(param->string, &rest);
-               if (*rest)
+               if (*rest || ctx->blocks > S64_MAX)
                        goto bad_value;
                ctx->seen |= SHMEM_SEEN_BLOCKS;
                break;
@@ -3475,10 +3514,7 @@ static int shmem_reconfigure(struct fs_context *fc)
 
        raw_spin_lock(&sbinfo->stat_lock);
        inodes = sbinfo->max_inodes - sbinfo->free_inodes;
-       if (ctx->blocks > S64_MAX) {
-               err = "Number of blocks too large";
-               goto out;
-       }
+
        if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) {
                if (!sbinfo->max_blocks) {
                        err = "Cannot retroactively limit size";