Merge tag 'amd-drm-fixes-5.9-2020-08-20' of git://people.freedesktop.org/~agd5f/linux...
[linux-2.6-microblaze.git] / mm / shmem.c
index 585a82d..271548c 100644 (file)
@@ -114,11 +114,13 @@ struct shmem_options {
        kuid_t uid;
        kgid_t gid;
        umode_t mode;
+       bool full_inums;
        int huge;
        int seen;
 #define SHMEM_SEEN_BLOCKS 1
 #define SHMEM_SEEN_INODES 2
 #define SHMEM_SEEN_HUGE 4
+#define SHMEM_SEEN_INUMS 8
 };
 
 #ifdef CONFIG_TMPFS
@@ -286,12 +288,17 @@ static int shmem_reserve_inode(struct super_block *sb, ino_t *inop)
                        ino = sbinfo->next_ino++;
                        if (unlikely(is_zero_ino(ino)))
                                ino = sbinfo->next_ino++;
-                       if (unlikely(ino > UINT_MAX)) {
+                       if (unlikely(!sbinfo->full_inums &&
+                                    ino > UINT_MAX)) {
                                /*
                                 * Emulate get_next_ino uint wraparound for
                                 * compatibility
                                 */
-                               ino = 1;
+                               if (IS_ENABLED(CONFIG_64BIT))
+                                       pr_warn("%s: inode number overflow on device %d, consider using inode64 mount option\n",
+                                               __func__, MINOR(sb->s_dev));
+                               sbinfo->next_ino = 1;
+                               ino = sbinfo->next_ino++;
                        }
                        *inop = ino;
                }
@@ -304,6 +311,10 @@ static int shmem_reserve_inode(struct super_block *sb, ino_t *inop)
                 * unknown contexts. As such, use a per-cpu batched allocator
                 * which doesn't require the per-sb stat_lock unless we are at
                 * the batch boundary.
+                *
+                * We don't need to worry about inode{32,64} since SB_KERNMOUNT
+                * shmem mounts are not exposed to userspace, so we don't need
+                * to worry about things like glibc compatibility.
                 */
                ino_t *next_ino;
                next_ino = per_cpu_ptr(sbinfo->ino_batch, get_cpu());
@@ -1423,7 +1434,8 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
                list_add(&info->swaplist, &shmem_swaplist);
 
        if (add_to_swap_cache(page, swap,
-                       __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN) == 0) {
+                       __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN,
+                       NULL) == 0) {
                spin_lock_irq(&info->lock);
                shmem_recalc_inode(inode);
                info->swapped++;
@@ -1674,7 +1686,7 @@ static int shmem_replace_page(struct page **pagep, gfp_t gfp,
  * Swap in the page pointed to by *pagep.
  * Caller has to make sure that *pagep contains a valid swapped page.
  * Returns 0 and the page in pagep if success. On failure, returns the
- * the error code and NULL in *pagep.
+ * error code and NULL in *pagep.
  */
 static int shmem_swapin_page(struct inode *inode, pgoff_t index,
                             struct page **pagep, enum sgp_type sgp,
@@ -3397,6 +3409,8 @@ enum shmem_param {
        Opt_nr_inodes,
        Opt_size,
        Opt_uid,
+       Opt_inode32,
+       Opt_inode64,
 };
 
 static const struct constant_table shmem_param_enums_huge[] = {
@@ -3416,6 +3430,8 @@ const struct fs_parameter_spec shmem_fs_parameters[] = {
        fsparam_string("nr_inodes",     Opt_nr_inodes),
        fsparam_string("size",          Opt_size),
        fsparam_u32   ("uid",           Opt_uid),
+       fsparam_flag  ("inode32",       Opt_inode32),
+       fsparam_flag  ("inode64",       Opt_inode64),
        {}
 };
 
@@ -3487,6 +3503,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
                        break;
                }
                goto unsupported_parameter;
+       case Opt_inode32:
+               ctx->full_inums = false;
+               ctx->seen |= SHMEM_SEEN_INUMS;
+               break;
+       case Opt_inode64:
+               if (sizeof(ino_t) < 8) {
+                       return invalfc(fc,
+                                      "Cannot use inode64 with <64bit inums in kernel\n");
+               }
+               ctx->full_inums = true;
+               ctx->seen |= SHMEM_SEEN_INUMS;
+               break;
        }
        return 0;
 
@@ -3578,8 +3606,16 @@ static int shmem_reconfigure(struct fs_context *fc)
                }
        }
 
+       if ((ctx->seen & SHMEM_SEEN_INUMS) && !ctx->full_inums &&
+           sbinfo->next_ino > UINT_MAX) {
+               err = "Current inum too high to switch to 32-bit inums";
+               goto out;
+       }
+
        if (ctx->seen & SHMEM_SEEN_HUGE)
                sbinfo->huge = ctx->huge;
+       if (ctx->seen & SHMEM_SEEN_INUMS)
+               sbinfo->full_inums = ctx->full_inums;
        if (ctx->seen & SHMEM_SEEN_BLOCKS)
                sbinfo->max_blocks  = ctx->blocks;
        if (ctx->seen & SHMEM_SEEN_INODES) {
@@ -3619,6 +3655,29 @@ static int shmem_show_options(struct seq_file *seq, struct dentry *root)
        if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID))
                seq_printf(seq, ",gid=%u",
                                from_kgid_munged(&init_user_ns, sbinfo->gid));
+
+       /*
+        * Showing inode{64,32} might be useful even if it's the system default,
+        * since then people don't have to resort to checking both here and
+        * /proc/config.gz to confirm 64-bit inums were successfully applied
+        * (which may not even exist if IKCONFIG_PROC isn't enabled).
+        *
+        * We hide it when inode64 isn't the default and we are using 32-bit
+        * inodes, since that probably just means the feature isn't even under
+        * consideration.
+        *
+        * As such:
+        *
+        *                     +-----------------+-----------------+
+        *                     | TMPFS_INODE64=y | TMPFS_INODE64=n |
+        *  +------------------+-----------------+-----------------+
+        *  | full_inums=true  | show            | show            |
+        *  | full_inums=false | show            | hide            |
+        *  +------------------+-----------------+-----------------+
+        *
+        */
+       if (IS_ENABLED(CONFIG_TMPFS_INODE64) || sbinfo->full_inums)
+               seq_printf(seq, ",inode%d", (sbinfo->full_inums ? 64 : 32));
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
        /* Rightly or wrongly, show huge mount option unmasked by shmem_huge */
        if (sbinfo->huge)
@@ -3667,6 +3726,8 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
                        ctx->blocks = shmem_default_max_blocks();
                if (!(ctx->seen & SHMEM_SEEN_INODES))
                        ctx->inodes = shmem_default_max_inodes();
+               if (!(ctx->seen & SHMEM_SEEN_INUMS))
+                       ctx->full_inums = IS_ENABLED(CONFIG_TMPFS_INODE64);
        } else {
                sb->s_flags |= SB_NOUSER;
        }
@@ -3684,6 +3745,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
        }
        sbinfo->uid = ctx->uid;
        sbinfo->gid = ctx->gid;
+       sbinfo->full_inums = ctx->full_inums;
        sbinfo->mode = ctx->mode;
        sbinfo->huge = ctx->huge;
        sbinfo->mpol = ctx->mpol;
@@ -4184,7 +4246,7 @@ EXPORT_SYMBOL_GPL(shmem_file_setup_with_mnt);
 
 /**
  * shmem_zero_setup - setup a shared anonymous mapping
- * @vma: the vma to be mmapped is prepared by do_mmap_pgoff
+ * @vma: the vma to be mmapped is prepared by do_mmap
  */
 int shmem_zero_setup(struct vm_area_struct *vma)
 {