Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  shmem.c   Sprache: C

 
/*
 * Resizable virtual memory filesystem for Linux.
 *
 * Copyright (C) 2000 Linus Torvalds.
 *  2000 Transmeta Corp.
 *  2000-2001 Christoph Rohland
 *  2000-2001 SAP AG
 *  2002 Red Hat Inc.
 * Copyright (C) 2002-2011 Hugh Dickins.
 * Copyright (C) 2011 Google Inc.
 * Copyright (C) 2002-2005 VERITAS Software Corporation.
 * Copyright (C) 2004 Andi Kleen, SuSE Labs
 *
 * Extended attribute support for tmpfs:
 * Copyright (c) 2004, Luke Kenneth Casson Leighton <lkcl@lkcl.net>
 * Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
 *
 * tiny-shmem:
 * Copyright (c) 2004, 2008 Matt Mackall <mpm@selenic.com>
 *
 * This file is released under the GPL.
 */


#include <linux/fs.h>
#include <linux/init.h>
#include <linux/vfs.h>
#include <linux/mount.h>
#include <linux/ramfs.h>
#include <linux/pagemap.h>
#include <linux/file.h>
#include <linux/fileattr.h>
#include <linux/mm.h>
#include <linux/random.h>
#include <linux/sched/signal.h>
#include <linux/export.h>
#include <linux/shmem_fs.h>
#include <linux/swap.h>
#include <linux/uio.h>
#include <linux/hugetlb.h>
#include <linux/fs_parser.h>
#include <linux/swapfile.h>
#include <linux/iversion.h>
#include <linux/unicode.h>
#include "swap.h"

static struct vfsmount *shm_mnt __ro_after_init;

#ifdef CONFIG_SHMEM
/*
 * This virtual memory filesystem is heavily based on the ramfs. It
 * extends ramfs by the ability to use swap and honor resource limits
 * which makes it a completely usable filesystem.
 */


#include <linux/xattr.h>
#include <linux/exportfs.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/backing-dev.h>
#include <linux/writeback.h>
#include <linux/pagevec.h>
#include <linux/percpu_counter.h>
#include <linux/falloc.h>
#include <linux/splice.h>
#include <linux/security.h>
#include <linux/swapops.h>
#include <linux/mempolicy.h>
#include <linux/namei.h>
#include <linux/ctype.h>
#include <linux/migrate.h>
#include <linux/highmem.h>
#include <linux/seq_file.h>
#include <linux/magic.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <uapi/linux/memfd.h>
#include <linux/rmap.h>
#include <linux/uuid.h>
#include <linux/quotaops.h>
#include <linux/rcupdate_wait.h>

#include <linux/uaccess.h>

#include "internal.h"

#define VM_ACCT(size)    (PAGE_ALIGN(size) >> PAGE_SHIFT)

/* Pretend that each entry is of this size in directory's i_size */
#define BOGO_DIRENT_SIZE 20

/* Pretend that one inode + its dentry occupy this much memory */
#define BOGO_INODE_SIZE 1024

/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
#define SHORT_SYMLINK_LEN 128

/*
 * shmem_fallocate communicates with shmem_fault or shmem_writeout via
 * inode->i_private (with i_rwsem making sure that it has only one user at
 * a time): we would prefer not to enlarge the shmem inode just for that.
 */

struct shmem_falloc {
 wait_queue_head_t *waitq; /* faults into hole wait for punch to end */
 pgoff_t start;  /* start of range currently being fallocated */
 pgoff_t next;  /* the next page offset to be fallocated */
 pgoff_t nr_falloced; /* how many new pages have been fallocated */
 pgoff_t nr_unswapped; /* how often writeout refused to swap out */
};

struct shmem_options {
 unsigned long long blocks;
 unsigned long long inodes;
 struct mempolicy *mpol;
 kuid_t uid;
 kgid_t gid;
 umode_t mode;
 bool full_inums;
 int huge;
 int seen;
 bool noswap;
 unsigned short quota_types;
 struct shmem_quota_limits qlimits;
#if IS_ENABLED(CONFIG_UNICODE)
 struct unicode_map *encoding;
 bool strict_encoding;
#endif
#define SHMEM_SEEN_BLOCKS 1
#define SHMEM_SEEN_INODES 2
#define SHMEM_SEEN_HUGE 4
#define SHMEM_SEEN_INUMS 8
#define SHMEM_SEEN_NOSWAP 16
#define SHMEM_SEEN_QUOTA 32
};

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
static unsigned long huge_shmem_orders_always __read_mostly;
static unsigned long huge_shmem_orders_madvise __read_mostly;
static unsigned long huge_shmem_orders_inherit __read_mostly;
static unsigned long huge_shmem_orders_within_size __read_mostly;
static bool shmem_orders_configured __initdata;
#endif

#ifdef CONFIG_TMPFS
static unsigned long shmem_default_max_blocks(void)
{
 return totalram_pages() / 2;
}

static unsigned long shmem_default_max_inodes(void)
{
 unsigned long nr_pages = totalram_pages();

 return min3(nr_pages - totalhigh_pages(), nr_pages / 2,
   ULONG_MAX / BOGO_INODE_SIZE);
}
#endif

static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
   struct folio **foliop, enum sgp_type sgp, gfp_t gfp,
   struct vm_area_struct *vma, vm_fault_t *fault_type);

static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb)
{
 return sb->s_fs_info;
}

/*
 * shmem_file_setup pre-accounts the whole fixed size of a VM object,
 * for shared memory and for shared anonymous (/dev/zero) mappings
 * (unless MAP_NORESERVE and sysctl_overcommit_memory <= 1),
 * consistent with the pre-accounting of private mappings ...
 */

static inline int shmem_acct_size(unsigned long flags, loff_t size)
{
 return (flags & VM_NORESERVE) ?
  0 : security_vm_enough_memory_mm(current->mm, VM_ACCT(size));
}

static inline void shmem_unacct_size(unsigned long flags, loff_t size)
{
 if (!(flags & VM_NORESERVE))
  vm_unacct_memory(VM_ACCT(size));
}

static inline int shmem_reacct_size(unsigned long flags,
  loff_t oldsize, loff_t newsize)
{
 if (!(flags & VM_NORESERVE)) {
  if (VM_ACCT(newsize) > VM_ACCT(oldsize))
   return security_vm_enough_memory_mm(current->mm,
     VM_ACCT(newsize) - VM_ACCT(oldsize));
  else if (VM_ACCT(newsize) < VM_ACCT(oldsize))
   vm_unacct_memory(VM_ACCT(oldsize) - VM_ACCT(newsize));
 }
 return 0;
}

/*
 * ... whereas tmpfs objects are accounted incrementally as
 * pages are allocated, in order to allow large sparse files.
 * shmem_get_folio reports shmem_acct_blocks failure as -ENOSPC not -ENOMEM,
 * so that a failure on a sparse tmpfs mapping will give SIGBUS not OOM.
 */

static inline int shmem_acct_blocks(unsigned long flags, long pages)
{
 if (!(flags & VM_NORESERVE))
  return 0;

 return security_vm_enough_memory_mm(current->mm,
   pages * VM_ACCT(PAGE_SIZE));
}

static inline void shmem_unacct_blocks(unsigned long flags, long pages)
{
 if (flags & VM_NORESERVE)
  vm_unacct_memory(pages * VM_ACCT(PAGE_SIZE));
}

static int shmem_inode_acct_blocks(struct inode *inode, long pages)
{
 struct shmem_inode_info *info = SHMEM_I(inode);
 struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 int err = -ENOSPC;

 if (shmem_acct_blocks(info->flags, pages))
  return err;

 might_sleep(); /* when quotas */
 if (sbinfo->max_blocks) {
  if (!percpu_counter_limited_add(&sbinfo->used_blocks,
      sbinfo->max_blocks, pages))
   goto unacct;

  err = dquot_alloc_block_nodirty(inode, pages);
  if (err) {
   percpu_counter_sub(&sbinfo->used_blocks, pages);
   goto unacct;
  }
 } else {
  err = dquot_alloc_block_nodirty(inode, pages);
  if (err)
   goto unacct;
 }

 return 0;

unacct:
 shmem_unacct_blocks(info->flags, pages);
 return err;
}

static void shmem_inode_unacct_blocks(struct inode *inode, long pages)
{
 struct shmem_inode_info *info = SHMEM_I(inode);
 struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);

 might_sleep(); /* when quotas */
 dquot_free_block_nodirty(inode, pages);

 if (sbinfo->max_blocks)
  percpu_counter_sub(&sbinfo->used_blocks, pages);
 shmem_unacct_blocks(info->flags, pages);
}

static const struct super_operations shmem_ops;
static const struct address_space_operations shmem_aops;
static const struct file_operations shmem_file_operations;
static const struct inode_operations shmem_inode_operations;
static const struct inode_operations shmem_dir_inode_operations;
static const struct inode_operations shmem_special_inode_operations;
static const struct vm_operations_struct shmem_vm_ops;
static const struct vm_operations_struct shmem_anon_vm_ops;
static struct file_system_type shmem_fs_type;

bool shmem_mapping(struct address_space *mapping)
{
 return mapping->a_ops == &shmem_aops;
}
EXPORT_SYMBOL_GPL(shmem_mapping);

bool vma_is_anon_shmem(struct vm_area_struct *vma)
{
 return vma->vm_ops == &shmem_anon_vm_ops;
}

bool vma_is_shmem(struct vm_area_struct *vma)
{
 return vma_is_anon_shmem(vma) || vma->vm_ops == &shmem_vm_ops;
}

static LIST_HEAD(shmem_swaplist);
static DEFINE_SPINLOCK(shmem_swaplist_lock);

#ifdef CONFIG_TMPFS_QUOTA

static int shmem_enable_quotas(struct super_block *sb,
          unsigned short quota_types)
{
 int type, err = 0;

 sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY;
 for (type = 0; type < SHMEM_MAXQUOTAS; type++) {
  if (!(quota_types & (1 << type)))
   continue;
  err = dquot_load_quota_sb(sb, type, QFMT_SHMEM,
       DQUOT_USAGE_ENABLED |
       DQUOT_LIMITS_ENABLED);
  if (err)
   goto out_err;
 }
 return 0;

out_err:
 pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n",
  type, err);
 for (type--; type >= 0; type--)
  dquot_quota_off(sb, type);
 return err;
}

static void shmem_disable_quotas(struct super_block *sb)
{
 int type;

 for (type = 0; type < SHMEM_MAXQUOTAS; type++)
  dquot_quota_off(sb, type);
}

static struct dquot __rcu **shmem_get_dquots(struct inode *inode)
{
 return SHMEM_I(inode)->i_dquot;
}
#endif /* CONFIG_TMPFS_QUOTA */

/*
 * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and
 * produces a novel ino for the newly allocated inode.
 *
 * It may also be called when making a hard link to permit the space needed by
 * each dentry. However, in that case, no new inode number is needed since that
 * internally draws from another pool of inode numbers (currently global
 * get_next_ino()). This case is indicated by passing NULL as inop.
 */

#define SHMEM_INO_BATCH 1024
static int shmem_reserve_inode(struct super_block *sb, ino_t *inop)
{
 struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 ino_t ino;

 if (!(sb->s_flags & SB_KERNMOUNT)) {
  raw_spin_lock(&sbinfo->stat_lock);
  if (sbinfo->max_inodes) {
   if (sbinfo->free_ispace < BOGO_INODE_SIZE) {
    raw_spin_unlock(&sbinfo->stat_lock);
    return -ENOSPC;
   }
   sbinfo->free_ispace -= BOGO_INODE_SIZE;
  }
  if (inop) {
   ino = sbinfo->next_ino++;
   if (unlikely(is_zero_ino(ino)))
    ino = sbinfo->next_ino++;
   if (unlikely(!sbinfo->full_inums &&
         ino > UINT_MAX)) {
    /*
 * Emulate get_next_ino uint wraparound for
 * compatibility
 */

    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;
  }
  raw_spin_unlock(&sbinfo->stat_lock);
 } else if (inop) {
  /*
 * __shmem_file_setup, one of our callers, is lock-free: it
 * doesn't hold stat_lock in shmem_reserve_inode since
 * max_inodes is always 0, and is called from potentially
 * 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());
  ino = *next_ino;
  if (unlikely(ino % SHMEM_INO_BATCH == 0)) {
   raw_spin_lock(&sbinfo->stat_lock);
   ino = sbinfo->next_ino;
   sbinfo->next_ino += SHMEM_INO_BATCH;
   raw_spin_unlock(&sbinfo->stat_lock);
   if (unlikely(is_zero_ino(ino)))
    ino++;
  }
  *inop = ino;
  *next_ino = ++ino;
  put_cpu();
 }

 return 0;
}

static void shmem_free_inode(struct super_block *sb, size_t freed_ispace)
{
 struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 if (sbinfo->max_inodes) {
  raw_spin_lock(&sbinfo->stat_lock);
  sbinfo->free_ispace += BOGO_INODE_SIZE + freed_ispace;
  raw_spin_unlock(&sbinfo->stat_lock);
 }
}

/**
 * shmem_recalc_inode - recalculate the block usage of an inode
 * @inode: inode to recalc
 * @alloced: the change in number of pages allocated to inode
 * @swapped: the change in number of pages swapped from inode
 *
 * We have to calculate the free blocks since the mm can drop
 * undirtied hole pages behind our back.
 *
 * But normally   info->alloced == inode->i_mapping->nrpages + info->swapped
 * So mm freed is info->alloced - (inode->i_mapping->nrpages + info->swapped)
 *
 * Return: true if swapped was incremented from 0, for shmem_writeout().
 */

static bool shmem_recalc_inode(struct inode *inode, long alloced, long swapped)
{
 struct shmem_inode_info *info = SHMEM_I(inode);
 bool first_swapped = false;
 long freed;

 spin_lock(&info->lock);
 info->alloced += alloced;
 info->swapped += swapped;
 freed = info->alloced - info->swapped -
  READ_ONCE(inode->i_mapping->nrpages);
 /*
 * Special case: whereas normally shmem_recalc_inode() is called
 * after i_mapping->nrpages has already been adjusted (up or down),
 * shmem_writeout() has to raise swapped before nrpages is lowered -
 * to stop a racing shmem_recalc_inode() from thinking that a page has
 * been freed.  Compensate here, to avoid the need for a followup call.
 */

 if (swapped > 0) {
  if (info->swapped == swapped)
   first_swapped = true;
  freed += swapped;
 }
 if (freed > 0)
  info->alloced -= freed;
 spin_unlock(&info->lock);

 /* The quota case may block */
 if (freed > 0)
  shmem_inode_unacct_blocks(inode, freed);
 return first_swapped;
}

bool shmem_charge(struct inode *inode, long pages)
{
 struct address_space *mapping = inode->i_mapping;

 if (shmem_inode_acct_blocks(inode, pages))
  return false;

 /* nrpages adjustment first, then shmem_recalc_inode() when balanced */
 xa_lock_irq(&mapping->i_pages);
 mapping->nrpages += pages;
 xa_unlock_irq(&mapping->i_pages);

 shmem_recalc_inode(inode, pages, 0);
 return true;
}

void shmem_uncharge(struct inode *inode, long pages)
{
 /* pages argument is currently unused: keep it to help debugging */
 /* nrpages adjustment done by __filemap_remove_folio() or caller */

 shmem_recalc_inode(inode, 0, 0);
}

/*
 * Replace item expected in xarray by a new item, while holding xa_lock.
 */

static int shmem_replace_entry(struct address_space *mapping,
   pgoff_t index, void *expected, void *replacement)
{
 XA_STATE(xas, &mapping->i_pages, index);
 void *item;

 VM_BUG_ON(!expected);
 VM_BUG_ON(!replacement);
 item = xas_load(&xas);
 if (item != expected)
  return -ENOENT;
 xas_store(&xas, replacement);
 return 0;
}

/*
 * Sometimes, before we decide whether to proceed or to fail, we must check
 * that an entry was not already brought back or split by a racing thread.
 *
 * Checking folio is not enough: by the time a swapcache folio is locked, it
 * might be reused, and again be swapcache, using the same swap as before.
 * Returns the swap entry's order if it still presents, else returns -1.
 */

static int shmem_confirm_swap(struct address_space *mapping, pgoff_t index,
         swp_entry_t swap)
{
 XA_STATE(xas, &mapping->i_pages, index);
 int ret = -1;
 void *entry;

 rcu_read_lock();
 do {
  entry = xas_load(&xas);
  if (entry == swp_to_radix_entry(swap))
   ret = xas_get_order(&xas);
 } while (xas_retry(&xas, entry));
 rcu_read_unlock();
 return ret;
}

/*
 * Definitions for "huge tmpfs": tmpfs mounted with the huge= option
 *
 * SHMEM_HUGE_NEVER:
 * disables huge pages for the mount;
 * SHMEM_HUGE_ALWAYS:
 * enables huge pages for the mount;
 * SHMEM_HUGE_WITHIN_SIZE:
 * only allocate huge pages if the page will be fully within i_size,
 * also respect madvise() hints;
 * SHMEM_HUGE_ADVISE:
 * only allocate huge pages if requested with madvise();
 */


#define SHMEM_HUGE_NEVER 0
#define SHMEM_HUGE_ALWAYS 1
#define SHMEM_HUGE_WITHIN_SIZE 2
#define SHMEM_HUGE_ADVISE 3

/*
 * Special values.
 * Only can be set via /sys/kernel/mm/transparent_hugepage/shmem_enabled:
 *
 * SHMEM_HUGE_DENY:
 * disables huge on shm_mnt and all mounts, for emergency use;
 * SHMEM_HUGE_FORCE:
 * enables huge on shm_mnt and all mounts, w/o needing option, for testing;
 *
 */

#define SHMEM_HUGE_DENY  (-1)
#define SHMEM_HUGE_FORCE (-2)

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
/* ifdef here to avoid bloating shmem.o when not necessary */

static int shmem_huge __read_mostly = SHMEM_HUGE_NEVER;
static int tmpfs_huge __read_mostly = SHMEM_HUGE_NEVER;

/**
 * shmem_mapping_size_orders - Get allowable folio orders for the given file size.
 * @mapping: Target address_space.
 * @index: The page index.
 * @write_end: end of a write, could extend inode size.
 *
 * This returns huge orders for folios (when supported) based on the file size
 * which the mapping currently allows at the given index. The index is relevant
 * due to alignment considerations the mapping might have. The returned order
 * may be less than the size passed.
 *
 * Return: The orders.
 */

static inline unsigned int
shmem_mapping_size_orders(struct address_space *mapping, pgoff_t index, loff_t write_end)
{
 unsigned int order;
 size_t size;

 if (!mapping_large_folio_support(mapping) || !write_end)
  return 0;

 /* Calculate the write size based on the write_end */
 size = write_end - (index << PAGE_SHIFT);
 order = filemap_get_order(size);
 if (!order)
  return 0;

 /* If we're not aligned, allocate a smaller folio */
 if (index & ((1UL << order) - 1))
  order = __ffs(index);

 order = min_t(size_t, order, MAX_PAGECACHE_ORDER);
 return order > 0 ? BIT(order + 1) - 1 : 0;
}

static unsigned int shmem_get_orders_within_size(struct inode *inode,
  unsigned long within_size_orders, pgoff_t index,
  loff_t write_end)
{
 pgoff_t aligned_index;
 unsigned long order;
 loff_t i_size;

 order = highest_order(within_size_orders);
 while (within_size_orders) {
  aligned_index = round_up(index + 1, 1 << order);
  i_size = max(write_end, i_size_read(inode));
  i_size = round_up(i_size, PAGE_SIZE);
  if (i_size >> PAGE_SHIFT >= aligned_index)
   return within_size_orders;

  order = next_order(&within_size_orders, order);
 }

 return 0;
}

static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,
           loff_t write_end, bool shmem_huge_force,
           struct vm_area_struct *vma,
           vm_flags_t vm_flags)
{
 unsigned int maybe_pmd_order = HPAGE_PMD_ORDER > MAX_PAGECACHE_ORDER ?
  0 : BIT(HPAGE_PMD_ORDER);
 unsigned long within_size_orders;

 if (!S_ISREG(inode->i_mode))
  return 0;
 if (shmem_huge == SHMEM_HUGE_DENY)
  return 0;
 if (shmem_huge_force || shmem_huge == SHMEM_HUGE_FORCE)
  return maybe_pmd_order;

 /*
 * The huge order allocation for anon shmem is controlled through
 * the mTHP interface, so we still use PMD-sized huge order to
 * check whether global control is enabled.
 *
 * For tmpfs mmap()'s huge order, we still use PMD-sized order to
 * allocate huge pages due to lack of a write size hint.
 *
 * Otherwise, tmpfs will allow getting a highest order hint based on
 * the size of write and fallocate paths, then will try each allowable
 * huge orders.
 */

 switch (SHMEM_SB(inode->i_sb)->huge) {
 case SHMEM_HUGE_ALWAYS:
  if (vma)
   return maybe_pmd_order;

  return shmem_mapping_size_orders(inode->i_mapping, index, write_end);
 case SHMEM_HUGE_WITHIN_SIZE:
  if (vma)
   within_size_orders = maybe_pmd_order;
  else
   within_size_orders = shmem_mapping_size_orders(inode->i_mapping,
               index, write_end);

  within_size_orders = shmem_get_orders_within_size(inode, within_size_orders,
          index, write_end);
  if (within_size_orders > 0)
   return within_size_orders;

  fallthrough;
 case SHMEM_HUGE_ADVISE:
  if (vm_flags & VM_HUGEPAGE)
   return maybe_pmd_order;
  fallthrough;
 default:
  return 0;
 }
}

static int shmem_parse_huge(const char *str)
{
 int huge;

 if (!str)
  return -EINVAL;

 if (!strcmp(str, "never"))
  huge = SHMEM_HUGE_NEVER;
 else if (!strcmp(str, "always"))
  huge = SHMEM_HUGE_ALWAYS;
 else if (!strcmp(str, "within_size"))
  huge = SHMEM_HUGE_WITHIN_SIZE;
 else if (!strcmp(str, "advise"))
  huge = SHMEM_HUGE_ADVISE;
 else if (!strcmp(str, "deny"))
  huge = SHMEM_HUGE_DENY;
 else if (!strcmp(str, "force"))
  huge = SHMEM_HUGE_FORCE;
 else
  return -EINVAL;

 if (!has_transparent_hugepage() &&
     huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
  return -EINVAL;

 /* Do not override huge allocation policy with non-PMD sized mTHP */
 if (huge == SHMEM_HUGE_FORCE &&
     huge_shmem_orders_inherit != BIT(HPAGE_PMD_ORDER))
  return -EINVAL;

 return huge;
}

#if defined(CONFIG_SYSFS) || defined(CONFIG_TMPFS)
static const char *shmem_format_huge(int huge)
{
 switch (huge) {
 case SHMEM_HUGE_NEVER:
  return "never";
 case SHMEM_HUGE_ALWAYS:
  return "always";
 case SHMEM_HUGE_WITHIN_SIZE:
  return "within_size";
 case SHMEM_HUGE_ADVISE:
  return "advise";
 case SHMEM_HUGE_DENY:
  return "deny";
 case SHMEM_HUGE_FORCE:
  return "force";
 default:
  VM_BUG_ON(1);
  return "bad_val";
 }
}
#endif

static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
  struct shrink_control *sc, unsigned long nr_to_free)
{
 LIST_HEAD(list), *pos, *next;
 struct inode *inode;
 struct shmem_inode_info *info;
 struct folio *folio;
 unsigned long batch = sc ? sc->nr_to_scan : 128;
 unsigned long split = 0, freed = 0;

 if (list_empty(&sbinfo->shrinklist))
  return SHRINK_STOP;

 spin_lock(&sbinfo->shrinklist_lock);
 list_for_each_safe(pos, next, &sbinfo->shrinklist) {
  info = list_entry(pos, struct shmem_inode_info, shrinklist);

  /* pin the inode */
  inode = igrab(&info->vfs_inode);

  /* inode is about to be evicted */
  if (!inode) {
   list_del_init(&info->shrinklist);
   goto next;
  }

  list_move(&info->shrinklist, &list);
next:
  sbinfo->shrinklist_len--;
  if (!--batch)
   break;
 }
 spin_unlock(&sbinfo->shrinklist_lock);

 list_for_each_safe(pos, next, &list) {
  pgoff_t next, end;
  loff_t i_size;
  int ret;

  info = list_entry(pos, struct shmem_inode_info, shrinklist);
  inode = &info->vfs_inode;

  if (nr_to_free && freed >= nr_to_free)
   goto move_back;

  i_size = i_size_read(inode);
  folio = filemap_get_entry(inode->i_mapping, i_size / PAGE_SIZE);
  if (!folio || xa_is_value(folio))
   goto drop;

  /* No large folio at the end of the file: nothing to split */
  if (!folio_test_large(folio)) {
   folio_put(folio);
   goto drop;
  }

  /* Check if there is anything to gain from splitting */
  next = folio_next_index(folio);
  end = shmem_fallocend(inode, DIV_ROUND_UP(i_size, PAGE_SIZE));
  if (end <= folio->index || end >= next) {
   folio_put(folio);
   goto drop;
  }

  /*
 * 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 (!folio_trylock(folio)) {
   folio_put(folio);
   goto move_back;
  }

  ret = split_folio(folio);
  folio_unlock(folio);
  folio_put(folio);

  /* If split failed move the inode on the list back to shrinklist */
  if (ret)
   goto move_back;

  freed += next - end;
  split++;
drop:
  list_del_init(&info->shrinklist);
  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);
 }

 return split;
}

static long shmem_unused_huge_scan(struct super_block *sb,
  struct shrink_control *sc)
{
 struct shmem_sb_info *sbinfo = SHMEM_SB(sb);

 if (!READ_ONCE(sbinfo->shrinklist_len))
  return SHRINK_STOP;

 return shmem_unused_huge_shrink(sbinfo, sc, 0);
}

static long shmem_unused_huge_count(struct super_block *sb,
  struct shrink_control *sc)
{
 struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 return READ_ONCE(sbinfo->shrinklist_len);
}
#else /* !CONFIG_TRANSPARENT_HUGEPAGE */

#define shmem_huge SHMEM_HUGE_DENY

static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
  struct shrink_control *sc, unsigned long nr_to_free)
{
 return 0;
}

static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,
           loff_t write_end, bool shmem_huge_force,
           struct vm_area_struct *vma,
           vm_flags_t vm_flags)
{
 return 0;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

static void shmem_update_stats(struct folio *folio, int nr_pages)
{
 if (folio_test_pmd_mappable(folio))
  __lruvec_stat_mod_folio(folio, NR_SHMEM_THPS, nr_pages);
 __lruvec_stat_mod_folio(folio, NR_FILE_PAGES, nr_pages);
 __lruvec_stat_mod_folio(folio, NR_SHMEM, nr_pages);
}

/*
 * Somewhat like filemap_add_folio, but error if expected item has gone.
 */

static int shmem_add_to_page_cache(struct folio *folio,
       struct address_space *mapping,
       pgoff_t index, void *expected, gfp_t gfp)
{
 XA_STATE_ORDER(xas, &mapping->i_pages, index, folio_order(folio));
 unsigned long nr = folio_nr_pages(folio);
 swp_entry_t iter, swap;
 void *entry;

 VM_BUG_ON_FOLIO(index != round_down(index, nr), folio);
 VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 VM_BUG_ON_FOLIO(!folio_test_swapbacked(folio), folio);

 folio_ref_add(folio, nr);
 folio->mapping = mapping;
 folio->index = index;

 gfp &= GFP_RECLAIM_MASK;
 folio_throttle_swaprate(folio, gfp);
 swap = radix_to_swp_entry(expected);

 do {
  iter = swap;
  xas_lock_irq(&xas);
  xas_for_each_conflict(&xas, entry) {
   /*
 * The range must either be empty, or filled with
 * expected swap entries. Shmem swap entries are never
 * partially freed without split of both entry and
 * folio, so there shouldn't be any holes.
 */

   if (!expected || entry != swp_to_radix_entry(iter)) {
    xas_set_err(&xas, -EEXIST);
    goto unlock;
   }
   iter.val += 1 << xas_get_order(&xas);
  }
  if (expected && iter.val - nr != swap.val) {
   xas_set_err(&xas, -EEXIST);
   goto unlock;
  }
  xas_store(&xas, folio);
  if (xas_error(&xas))
   goto unlock;
  shmem_update_stats(folio, nr);
  mapping->nrpages += nr;
unlock:
  xas_unlock_irq(&xas);
 } while (xas_nomem(&xas, gfp));

 if (xas_error(&xas)) {
  folio->mapping = NULL;
  folio_ref_sub(folio, nr);
  return xas_error(&xas);
 }

 return 0;
}

/*
 * Somewhat like filemap_remove_folio, but substitutes swap for @folio.
 */

static void shmem_delete_from_page_cache(struct folio *folio, void *radswap)
{
 struct address_space *mapping = folio->mapping;
 long nr = folio_nr_pages(folio);
 int error;

 xa_lock_irq(&mapping->i_pages);
 error = shmem_replace_entry(mapping, folio->index, folio, radswap);
 folio->mapping = NULL;
 mapping->nrpages -= nr;
 shmem_update_stats(folio, -nr);
 xa_unlock_irq(&mapping->i_pages);
 folio_put_refs(folio, nr);
 BUG_ON(error);
}

/*
 * Remove swap entry from page cache, free the swap and its page cache. Returns
 * the number of pages being freed. 0 means entry not found in XArray (0 pages
 * being freed).
 */

static long shmem_free_swap(struct address_space *mapping,
       pgoff_t index, void *radswap)
{
 int order = xa_get_order(&mapping->i_pages, index);
 void *old;

 old = xa_cmpxchg_irq(&mapping->i_pages, index, radswap, NULL, 0);
 if (old != radswap)
  return 0;
 free_swap_and_cache_nr(radix_to_swp_entry(radswap), 1 << order);

 return 1 << order;
}

/*
 * Determine (in bytes) how many of the shmem object's pages mapped by the
 * given offsets are swapped out.
 *
 * This is safe to call without i_rwsem or the i_pages lock thanks to RCU,
 * as long as the inode doesn't go away and racy results are not a problem.
 */

unsigned long shmem_partial_swap_usage(struct address_space *mapping,
      pgoff_t start, pgoff_t end)
{
 XA_STATE(xas, &mapping->i_pages, start);
 struct page *page;
 unsigned long swapped = 0;
 unsigned long max = end - 1;

 rcu_read_lock();
 xas_for_each(&xas, page, max) {
  if (xas_retry(&xas, page))
   continue;
  if (xa_is_value(page))
   swapped += 1 << xas_get_order(&xas);
  if (xas.xa_index == max)
   break;
  if (need_resched()) {
   xas_pause(&xas);
   cond_resched_rcu();
  }
 }
 rcu_read_unlock();

 return swapped << PAGE_SHIFT;
}

/*
 * Determine (in bytes) how many of the shmem object's pages mapped by the
 * given vma is swapped out.
 *
 * This is safe to call without i_rwsem or the i_pages lock thanks to RCU,
 * as long as the inode doesn't go away and racy results are not a problem.
 */

unsigned long shmem_swap_usage(struct vm_area_struct *vma)
{
 struct inode *inode = file_inode(vma->vm_file);
 struct shmem_inode_info *info = SHMEM_I(inode);
 struct address_space *mapping = inode->i_mapping;
 unsigned long swapped;

 /* Be careful as we don't hold info->lock */
 swapped = READ_ONCE(info->swapped);

 /*
 * The easier cases are when the shmem object has nothing in swap, or
 * the vma maps it whole. Then we can simply use the stats that we
 * already track.
 */

 if (!swapped)
  return 0;

 if (!vma->vm_pgoff && vma->vm_end - vma->vm_start >= inode->i_size)
  return swapped << PAGE_SHIFT;

 /* Here comes the more involved part */
 return shmem_partial_swap_usage(mapping, vma->vm_pgoff,
     vma->vm_pgoff + vma_pages(vma));
}

/*
 * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists.
 */

void shmem_unlock_mapping(struct address_space *mapping)
{
 struct folio_batch fbatch;
 pgoff_t index = 0;

 folio_batch_init(&fbatch);
 /*
 * Minor point, but we might as well stop if someone else SHM_LOCKs it.
 */

 while (!mapping_unevictable(mapping) &&
        filemap_get_folios(mapping, &index, ~0UL, &fbatch)) {
  check_move_unevictable_folios(&fbatch);
  folio_batch_release(&fbatch);
  cond_resched();
 }
}

static struct folio *shmem_get_partial_folio(struct inode *inode, pgoff_t index)
{
 struct folio *folio;

 /*
 * At first avoid shmem_get_folio(,,,SGP_READ): that fails
 * beyond i_size, and reports fallocated folios as holes.
 */

 folio = filemap_get_entry(inode->i_mapping, index);
 if (!folio)
  return folio;
 if (!xa_is_value(folio)) {
  folio_lock(folio);
  if (folio->mapping == inode->i_mapping)
   return folio;
  /* The folio has been swapped out */
  folio_unlock(folio);
  folio_put(folio);
 }
 /*
 * But read a folio back from swap if any of it is within i_size
 * (although in some cases this is just a waste of time).
 */

 folio = NULL;
 shmem_get_folio(inode, index, 0, &folio, SGP_READ);
 return folio;
}

/*
 * Remove range of pages and swap entries from page cache, and free them.
 * If !unfalloc, truncate or punch hole; if unfalloc, undo failed fallocate.
 */

static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
         bool unfalloc)
{
 struct address_space *mapping = inode->i_mapping;
 struct shmem_inode_info *info = SHMEM_I(inode);
 pgoff_t start = (lstart + PAGE_SIZE - 1) >> PAGE_SHIFT;
 pgoff_t end = (lend + 1) >> PAGE_SHIFT;
 struct folio_batch fbatch;
 pgoff_t indices[PAGEVEC_SIZE];
 struct folio *folio;
 bool same_folio;
 long nr_swaps_freed = 0;
 pgoff_t index;
 int i;

 if (lend == -1)
  end = -1; /* unsigned, so actually very big */

 if (info->fallocend > start && info->fallocend <= end && !unfalloc)
  info->fallocend = start;

 folio_batch_init(&fbatch);
 index = start;
 while (index < end && find_lock_entries(mapping, &index, end - 1,
   &fbatch, indices)) {
  for (i = 0; i < folio_batch_count(&fbatch); i++) {
   folio = fbatch.folios[i];

   if (xa_is_value(folio)) {
    if (unfalloc)
     continue;
    nr_swaps_freed += shmem_free_swap(mapping,
       indices[i], folio);
    continue;
   }

   if (!unfalloc || !folio_test_uptodate(folio))
    truncate_inode_folio(mapping, folio);
   folio_unlock(folio);
  }
  folio_batch_remove_exceptionals(&fbatch);
  folio_batch_release(&fbatch);
  cond_resched();
 }

 /*
 * When undoing a failed fallocate, we want none of the partial folio
 * zeroing and splitting below, but shall want to truncate the whole
 * folio when !uptodate indicates that it was added by this fallocate,
 * even when [lstart, lend] covers only a part of the folio.
 */

 if (unfalloc)
  goto whole_folios;

 same_folio = (lstart >> PAGE_SHIFT) == (lend >> PAGE_SHIFT);
 folio = shmem_get_partial_folio(inode, lstart >> PAGE_SHIFT);
 if (folio) {
  same_folio = lend < folio_pos(folio) + folio_size(folio);
  folio_mark_dirty(folio);
  if (!truncate_inode_partial_folio(folio, lstart, lend)) {
   start = folio_next_index(folio);
   if (same_folio)
    end = folio->index;
  }
  folio_unlock(folio);
  folio_put(folio);
  folio = NULL;
 }

 if (!same_folio)
  folio = shmem_get_partial_folio(inode, lend >> PAGE_SHIFT);
 if (folio) {
  folio_mark_dirty(folio);
  if (!truncate_inode_partial_folio(folio, lstart, lend))
   end = folio->index;
  folio_unlock(folio);
  folio_put(folio);
 }

whole_folios:

 index = start;
 while (index < end) {
  cond_resched();

  if (!find_get_entries(mapping, &index, end - 1, &fbatch,
    indices)) {
   /* If all gone or hole-punch or unfalloc, we're done */
   if (index == start || end != -1)
    break;
   /* But if truncating, restart to make sure all gone */
   index = start;
   continue;
  }
  for (i = 0; i < folio_batch_count(&fbatch); i++) {
   folio = fbatch.folios[i];

   if (xa_is_value(folio)) {
    long swaps_freed;

    if (unfalloc)
     continue;
    swaps_freed = shmem_free_swap(mapping, indices[i], folio);
    if (!swaps_freed) {
     /* Swap was replaced by page: retry */
     index = indices[i];
     break;
    }
    nr_swaps_freed += swaps_freed;
    continue;
   }

   folio_lock(folio);

   if (!unfalloc || !folio_test_uptodate(folio)) {
    if (folio_mapping(folio) != mapping) {
     /* Page was replaced by swap: retry */
     folio_unlock(folio);
     index = indices[i];
     break;
    }
    VM_BUG_ON_FOLIO(folio_test_writeback(folio),
      folio);

    if (!folio_test_large(folio)) {
     truncate_inode_folio(mapping, folio);
    } else if (truncate_inode_partial_folio(folio, lstart, lend)) {
     /*
 * If we split a page, reset the loop so
 * that we pick up the new sub pages.
 * Otherwise the THP was entirely
 * dropped or the target range was
 * zeroed, so just continue the loop as
 * is.
 */

     if (!folio_test_large(folio)) {
      folio_unlock(folio);
      index = start;
      break;
     }
    }
   }
   folio_unlock(folio);
  }
  folio_batch_remove_exceptionals(&fbatch);
  folio_batch_release(&fbatch);
 }

 shmem_recalc_inode(inode, 0, -nr_swaps_freed);
}

void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
{
 shmem_undo_range(inode, lstart, lend, false);
 inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 inode_inc_iversion(inode);
}
EXPORT_SYMBOL_GPL(shmem_truncate_range);

static int shmem_getattr(struct mnt_idmap *idmap,
    const struct path *path, struct kstat *stat,
    u32 request_mask, unsigned int query_flags)
{
 struct inode *inode = path->dentry->d_inode;
 struct shmem_inode_info *info = SHMEM_I(inode);

 if (info->alloced - info->swapped != inode->i_mapping->nrpages)
  shmem_recalc_inode(inode, 0, 0);

 if (info->fsflags & FS_APPEND_FL)
  stat->attributes |= STATX_ATTR_APPEND;
 if (info->fsflags & FS_IMMUTABLE_FL)
  stat->attributes |= STATX_ATTR_IMMUTABLE;
 if (info->fsflags & FS_NODUMP_FL)
  stat->attributes |= STATX_ATTR_NODUMP;
 stat->attributes_mask |= (STATX_ATTR_APPEND |
   STATX_ATTR_IMMUTABLE |
   STATX_ATTR_NODUMP);
 generic_fillattr(idmap, request_mask, inode, stat);

 if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
  stat->blksize = HPAGE_PMD_SIZE;

 if (request_mask & STATX_BTIME) {
  stat->result_mask |= STATX_BTIME;
  stat->btime.tv_sec = info->i_crtime.tv_sec;
  stat->btime.tv_nsec = info->i_crtime.tv_nsec;
 }

 return 0;
}

static int shmem_setattr(struct mnt_idmap *idmap,
    struct dentry *dentry, struct iattr *attr)
{
 struct inode *inode = d_inode(dentry);
 struct shmem_inode_info *info = SHMEM_I(inode);
 int error;
 bool update_mtime = false;
 bool update_ctime = true;

 error = setattr_prepare(idmap, dentry, attr);
 if (error)
  return error;

 if ((info->seals & F_SEAL_EXEC) && (attr->ia_valid & ATTR_MODE)) {
  if ((inode->i_mode ^ attr->ia_mode) & 0111) {
   return -EPERM;
  }
 }

 if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
  loff_t oldsize = inode->i_size;
  loff_t newsize = attr->ia_size;

  /* protected by i_rwsem */
  if ((newsize < oldsize && (info->seals & F_SEAL_SHRINK)) ||
      (newsize > oldsize && (info->seals & F_SEAL_GROW)))
   return -EPERM;

  if (newsize != oldsize) {
   error = shmem_reacct_size(SHMEM_I(inode)->flags,
     oldsize, newsize);
   if (error)
    return error;
   i_size_write(inode, newsize);
   update_mtime = true;
  } else {
   update_ctime = false;
  }
  if (newsize <= oldsize) {
   loff_t holebegin = round_up(newsize, PAGE_SIZE);
   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 */
   if (oldsize > holebegin)
    unmap_mapping_range(inode->i_mapping,
       holebegin, 0, 1);
  }
 }

 if (is_quota_modification(idmap, inode, attr)) {
  error = dquot_initialize(inode);
  if (error)
   return error;
 }

 /* Transfer quota accounting */
 if (i_uid_needs_update(idmap, attr, inode) ||
     i_gid_needs_update(idmap, attr, inode)) {
  error = dquot_transfer(idmap, inode, attr);
  if (error)
   return error;
 }

 setattr_copy(idmap, inode, attr);
 if (attr->ia_valid & ATTR_MODE)
  error = posix_acl_chmod(idmap, dentry, inode->i_mode);
 if (!error && update_ctime) {
  inode_set_ctime_current(inode);
  if (update_mtime)
   inode_set_mtime_to_ts(inode, inode_get_ctime(inode));
  inode_inc_iversion(inode);
 }
 return error;
}

static void shmem_evict_inode(struct inode *inode)
{
 struct shmem_inode_info *info = SHMEM_I(inode);
 struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 size_t freed = 0;

 if (shmem_mapping(inode->i_mapping)) {
  shmem_unacct_size(info->flags, inode->i_size);
  inode->i_size = 0;
  mapping_set_exiting(inode->i_mapping);
  shmem_truncate_range(inode, 0, (loff_t)-1);
  if (!list_empty(&info->shrinklist)) {
   spin_lock(&sbinfo->shrinklist_lock);
   if (!list_empty(&info->shrinklist)) {
    list_del_init(&info->shrinklist);
    sbinfo->shrinklist_len--;
   }
   spin_unlock(&sbinfo->shrinklist_lock);
  }
  while (!list_empty(&info->swaplist)) {
   /* Wait while shmem_unuse() is scanning this inode... */
   wait_var_event(&info->stop_eviction,
           !atomic_read(&info->stop_eviction));
   spin_lock(&shmem_swaplist_lock);
   /* ...but beware of the race if we peeked too early */
   if (!atomic_read(&info->stop_eviction))
    list_del_init(&info->swaplist);
   spin_unlock(&shmem_swaplist_lock);
  }
 }

 simple_xattrs_free(&info->xattrs, sbinfo->max_inodes ? &freed : NULL);
 shmem_free_inode(inode->i_sb, freed);
 WARN_ON(inode->i_blocks);
 clear_inode(inode);
#ifdef CONFIG_TMPFS_QUOTA
 dquot_free_inode(inode);
 dquot_drop(inode);
#endif
}

static unsigned int shmem_find_swap_entries(struct address_space *mapping,
    pgoff_t start, struct folio_batch *fbatch,
    pgoff_t *indices, unsigned int type)
{
 XA_STATE(xas, &mapping->i_pages, start);
 struct folio *folio;
 swp_entry_t entry;

 rcu_read_lock();
 xas_for_each(&xas, folio, ULONG_MAX) {
  if (xas_retry(&xas, folio))
   continue;

  if (!xa_is_value(folio))
   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;

  indices[folio_batch_count(fbatch)] = xas.xa_index;
  if (!folio_batch_add(fbatch, folio))
   break;

  if (need_resched()) {
   xas_pause(&xas);
   cond_resched_rcu();
  }
 }
 rcu_read_unlock();

 return folio_batch_count(fbatch);
}

/*
 * Move the swapped pages for an inode to page cache. Returns the count
 * of pages swapped in, or the error in case of failure.
 */

static int shmem_unuse_swap_entries(struct inode *inode,
  struct folio_batch *fbatch, pgoff_t *indices)
{
 int i = 0;
 int ret = 0;
 int error = 0;
 struct address_space *mapping = inode->i_mapping;

 for (i = 0; i < folio_batch_count(fbatch); i++) {
  struct folio *folio = fbatch->folios[i];

  error = shmem_swapin_folio(inode, indices[i], &folio, SGP_CACHE,
     mapping_gfp_mask(mapping), NULL, NULL);
  if (error == 0) {
   folio_unlock(folio);
   folio_put(folio);
   ret++;
  }
  if (error == -ENOMEM)
   break;
  error = 0;
 }
 return error ? error : ret;
}

/*
 * 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)
{
 struct address_space *mapping = inode->i_mapping;
 pgoff_t start = 0;
 struct folio_batch fbatch;
 pgoff_t indices[PAGEVEC_SIZE];
 int ret = 0;

 do {
  folio_batch_init(&fbatch);
  if (!shmem_find_swap_entries(mapping, start, &fbatch,
          indices, type)) {
   ret = 0;
   break;
  }

  ret = shmem_unuse_swap_entries(inode, &fbatch, indices);
  if (ret < 0)
   break;

  start = indices[folio_batch_count(&fbatch) - 1];
 } while (true);

 return ret;
}

/*
 * Read all the shared memory data that resides in the swap
 * device 'type' back into memory, so the swap device can be
 * unused.
 */

int shmem_unuse(unsigned int type)
{
 struct shmem_inode_info *info, *next;
 int error = 0;

 if (list_empty(&shmem_swaplist))
  return 0;

 spin_lock(&shmem_swaplist_lock);
start_over:
 list_for_each_entry_safe(info, next, &shmem_swaplist, swaplist) {
  if (!info->swapped) {
   list_del_init(&info->swaplist);
   continue;
  }
  /*
 * Drop the swaplist mutex while searching the inode for swap;
 * but before doing so, make sure shmem_evict_inode() will not
 * remove placeholder inode from swaplist, nor let it be freed
 * (igrab() would protect from unlink, but not from unmount).
 */

  atomic_inc(&info->stop_eviction);
  spin_unlock(&shmem_swaplist_lock);

  error = shmem_unuse_inode(&info->vfs_inode, type);
  cond_resched();

  spin_lock(&shmem_swaplist_lock);
  if (atomic_dec_and_test(&info->stop_eviction))
   wake_up_var(&info->stop_eviction);
  if (error)
   break;
  if (list_empty(&info->swaplist))
   goto start_over;
  next = list_next_entry(info, swaplist);
  if (!info->swapped)
   list_del_init(&info->swaplist);
 }
 spin_unlock(&shmem_swaplist_lock);

 return error;
}

/**
 * shmem_writeout - Write the folio to swap
 * @folio: The folio to write
 * @plug: swap plug
 * @folio_list: list to put back folios on split
 *
 * Move the folio from the page cache to the swap cache.
 */

int shmem_writeout(struct folio *folio, struct swap_iocb **plug,
  struct list_head *folio_list)
{
 struct address_space *mapping = folio->mapping;
 struct inode *inode = mapping->host;
 struct shmem_inode_info *info = SHMEM_I(inode);
 struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 pgoff_t index;
 int nr_pages;
 bool split = false;

 if ((info->flags & VM_LOCKED) || sbinfo->noswap)
  goto redirty;

 if (!total_swap_pages)
  goto redirty;

 /*
 * If CONFIG_THP_SWAP is not enabled, the large folio should be
 * split when swapping.
 *
 * And shrinkage of pages beyond i_size does not split swap, so
 * swapout of a large folio crossing i_size needs to split too
 * (unless fallocate has been used to preallocate beyond EOF).
 */

 if (folio_test_large(folio)) {
  index = shmem_fallocend(inode,
   DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE));
  if ((index > folio->index && index < folio_next_index(folio)) ||
      !IS_ENABLED(CONFIG_THP_SWAP))
   split = true;
 }

 if (split) {
try_split:
  /* Ensure the subpages are still dirty */
  folio_test_set_dirty(folio);
  if (split_folio_to_list(folio, folio_list))
   goto redirty;
  folio_clear_dirty(folio);
 }

 index = folio->index;
 nr_pages = folio_nr_pages(folio);

 /*
 * This is somewhat ridiculous, but without plumbing a SWAP_MAP_FALLOC
 * value into swapfile.c, the only way we can correctly account for a
 * fallocated folio arriving here is now to initialize it and write it.
 *
 * That's okay for a folio already fallocated earlier, but if we have
 * not yet completed the fallocation, then (a) we want to keep track
 * of this folio in case we have to undo it, and (b) it may not be a
 * good idea to continue anyway, once we're pushing into swap.  So
 * reactivate the folio, and let shmem_fallocate() quit when too many.
 */

 if (!folio_test_uptodate(folio)) {
  if (inode->i_private) {
   struct shmem_falloc *shmem_falloc;
   spin_lock(&inode->i_lock);
   shmem_falloc = inode->i_private;
   if (shmem_falloc &&
       !shmem_falloc->waitq &&
       index >= shmem_falloc->start &&
       index < shmem_falloc->next)
    shmem_falloc->nr_unswapped += nr_pages;
   else
    shmem_falloc = NULL;
   spin_unlock(&inode->i_lock);
   if (shmem_falloc)
    goto redirty;
  }
  folio_zero_range(folio, 0, folio_size(folio));
  flush_dcache_folio(folio);
  folio_mark_uptodate(folio);
 }

 if (!folio_alloc_swap(folio, __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN)) {
  bool first_swapped = shmem_recalc_inode(inode, 0, nr_pages);
  int error;

  /*
 * Add inode to shmem_unuse()'s list of swapped-out inodes,
 * if it's not already there.  Do it now before the folio is
 * removed from page cache, when its pagelock no longer
 * protects the inode from eviction.  And do it now, after
 * we've incremented swapped, because shmem_unuse() will
 * prune a !swapped inode from the swaplist.
 */

  if (first_swapped) {
   spin_lock(&shmem_swaplist_lock);
   if (list_empty(&info->swaplist))
    list_add(&info->swaplist, &shmem_swaplist);
   spin_unlock(&shmem_swaplist_lock);
  }

  swap_shmem_alloc(folio->swap, nr_pages);
  shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));

  BUG_ON(folio_mapped(folio));
  error = swap_writeout(folio, plug);
  if (error != AOP_WRITEPAGE_ACTIVATE) {
   /* folio has been unlocked */
   return error;
  }

  /*
 * The intention here is to avoid holding on to the swap when
 * zswap was unable to compress and unable to writeback; but
 * it will be appropriate if other reactivate cases are added.
 */

  error = shmem_add_to_page_cache(folio, mapping, index,
    swp_to_radix_entry(folio->swap),
    __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
  /* Swap entry might be erased by racing shmem_free_swap() */
  if (!error) {
   shmem_recalc_inode(inode, 0, -nr_pages);
   swap_free_nr(folio->swap, nr_pages);
  }

  /*
 * The delete_from_swap_cache() below could be left for
 * shrink_folio_list()'s folio_free_swap() to dispose of;
 * but I'm a little nervous about letting this folio out of
 * shmem_writeout() in a hybrid half-tmpfs-half-swap state
 * e.g. folio_mapping(folio) might give an unexpected answer.
 */

  delete_from_swap_cache(folio);
  goto redirty;
 }
 if (nr_pages > 1)
  goto try_split;
redirty:
 folio_mark_dirty(folio);
 return AOP_WRITEPAGE_ACTIVATE; /* Return with folio locked */
}
EXPORT_SYMBOL_GPL(shmem_writeout);

#if defined(CONFIG_NUMA) && defined(CONFIG_TMPFS)
static void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
{
 char buffer[64];

 if (!mpol || mpol->mode == MPOL_DEFAULT)
  return;  /* show nothing */

 mpol_to_str(buffer, sizeof(buffer), mpol);

 seq_printf(seq, ",mpol=%s", buffer);
}

static struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
{
 struct mempolicy *mpol = NULL;
 if (sbinfo->mpol) {
  raw_spin_lock(&sbinfo->stat_lock); /* prevent replace/use races */
  mpol = sbinfo->mpol;
  mpol_get(mpol);
  raw_spin_unlock(&sbinfo->stat_lock);
 }
 return mpol;
}
#else /* !CONFIG_NUMA || !CONFIG_TMPFS */
static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
{
}
static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
{
 return NULL;
}
#endif /* CONFIG_NUMA && CONFIG_TMPFS */

static struct mempolicy *shmem_get_pgoff_policy(struct shmem_inode_info *info,
   pgoff_t index, unsigned int order, pgoff_t *ilx);

static struct folio *shmem_swapin_cluster(swp_entry_t swap, gfp_t gfp,
   struct shmem_inode_info *info, pgoff_t index)
{
 struct mempolicy *mpol;
 pgoff_t ilx;
 struct folio *folio;

 mpol = shmem_get_pgoff_policy(info, index, 0, &ilx);
 folio = swap_cluster_readahead(swap, gfp, mpol, ilx);
 mpol_cond_put(mpol);

 return folio;
}

/*
 * Make sure huge_gfp is always more limited than limit_gfp.
 * Some of the flags set permissions, while others set limitations.
 */

static gfp_t limit_gfp_mask(gfp_t huge_gfp, gfp_t limit_gfp)
{
 gfp_t allowflags = __GFP_IO | __GFP_FS | __GFP_RECLAIM;
 gfp_t denyflags = __GFP_NOWARN | __GFP_NORETRY;
 gfp_t zoneflags = limit_gfp & GFP_ZONEMASK;
 gfp_t result = huge_gfp & ~(allowflags | GFP_ZONEMASK);

 /* Allow allocations only from the originally specified zones. */
 result |= zoneflags;

 /*
 * Minimize the result gfp by taking the union with the deny flags,
 * and the intersection of the allow flags.
 */

 result |= (limit_gfp & denyflags);
 result |= (huge_gfp & limit_gfp) & allowflags;

 return result;
}

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
bool shmem_hpage_pmd_enabled(void)
{
 if (shmem_huge == SHMEM_HUGE_DENY)
  return false;
 if (test_bit(HPAGE_PMD_ORDER, &huge_shmem_orders_always))
  return true;
 if (test_bit(HPAGE_PMD_ORDER, &huge_shmem_orders_madvise))
  return true;
 if (test_bit(HPAGE_PMD_ORDER, &huge_shmem_orders_within_size))
  return true;
 if (test_bit(HPAGE_PMD_ORDER, &huge_shmem_orders_inherit) &&
     shmem_huge != SHMEM_HUGE_NEVER)
  return true;

 return false;
}

unsigned long shmem_allowable_huge_orders(struct inode *inode,
    struct vm_area_struct *vma, pgoff_t index,
    loff_t write_end, bool shmem_huge_force)
{
 unsigned long mask = READ_ONCE(huge_shmem_orders_always);
 unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
 vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
 unsigned int global_orders;

 if (thp_disabled_by_hw() || (vma && vma_thp_disabled(vma, vm_flags)))
  return 0;

 global_orders = shmem_huge_global_enabled(inode, index, write_end,
        shmem_huge_force, vma, vm_flags);
 /* Tmpfs huge pages allocation */
 if (!vma || !vma_is_anon_shmem(vma))
  return global_orders;

 /*
 * Following the 'deny' semantics of the top level, force the huge
 * option off from all mounts.
 */

 if (shmem_huge == SHMEM_HUGE_DENY)
  return 0;

 /*
 * Only allow inherit orders if the top-level value is 'force', which
 * means non-PMD sized THP can not override 'huge' mount option now.
 */

 if (shmem_huge == SHMEM_HUGE_FORCE)
  return READ_ONCE(huge_shmem_orders_inherit);

 /* Allow mTHP that will be fully within i_size. */
 mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);

 if (vm_flags & VM_HUGEPAGE)
  mask |= READ_ONCE(huge_shmem_orders_madvise);

 if (global_orders > 0)
  mask |= READ_ONCE(huge_shmem_orders_inherit);

 return THP_ORDERS_ALL_FILE_DEFAULT & mask;
}

static unsigned long shmem_suitable_orders(struct inode *inode, struct vm_fault *vmf,
        struct address_space *mapping, pgoff_t index,
        unsigned long orders)
{
 struct vm_area_struct *vma = vmf ? vmf->vma : NULL;
 pgoff_t aligned_index;
 unsigned long pages;
 int order;

 if (vma) {
  orders = thp_vma_suitable_orders(vma, vmf->address, orders);
  if (!orders)
   return 0;
 }

 /* Find the highest order that can add into the page cache */
 order = highest_order(orders);
 while (orders) {
  pages = 1UL << order;
  aligned_index = round_down(index, pages);
  /*
 * Check for conflict before waiting on a huge allocation.
 * Conflict might be that a huge page has just been allocated
 * and added to page cache by a racing thread, or that there
 * is already at least one small page in the huge extent.
 * Be careful to retry when appropriate, but not forever!
 * Elsewhere -EEXIST would be the right code, but not here.
 */

  if (!xa_find(&mapping->i_pages, &aligned_index,
        aligned_index + pages - 1, XA_PRESENT))
   break;
  order = next_order(&orders, order);
 }

 return orders;
}
#else
static unsigned long shmem_suitable_orders(struct inode *inode, struct vm_fault *vmf,
        struct address_space *mapping, pgoff_t index,
        unsigned long orders)
{
 return 0;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

static struct folio *shmem_alloc_folio(gfp_t gfp, int order,
  struct shmem_inode_info *info, pgoff_t index)
{
 struct mempolicy *mpol;
 pgoff_t ilx;
 struct folio *folio;

 mpol = shmem_get_pgoff_policy(info, index, order, &ilx);
 folio = folio_alloc_mpol(gfp, order, mpol, ilx, numa_node_id());
 mpol_cond_put(mpol);

 return folio;
}

static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
  gfp_t gfp, struct inode *inode, pgoff_t index,
  struct mm_struct *fault_mm, unsigned long orders)
{
 struct address_space *mapping = inode->i_mapping;
 struct shmem_inode_info *info = SHMEM_I(inode);
 unsigned long suitable_orders = 0;
 struct folio *folio = NULL;
 pgoff_t aligned_index;
 long pages;
 int error, order;

 if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
  orders = 0;

 if (orders > 0) {
  suitable_orders = shmem_suitable_orders(inode, vmf,
       mapping, index, orders);

  order = highest_order(suitable_orders);
  while (suitable_orders) {
   pages = 1UL << order;
   aligned_index = round_down(index, pages);
   folio = shmem_alloc_folio(gfp, order, info, aligned_index);
   if (folio) {
    index = aligned_index;
    goto allocated;
   }

   if (pages == HPAGE_PMD_NR)
    count_vm_event(THP_FILE_FALLBACK);
   count_mthp_stat(order, MTHP_STAT_SHMEM_FALLBACK);
   order = next_order(&suitable_orders, order);
  }
 } else {
  pages = 1;
  folio = shmem_alloc_folio(gfp, 0, info, index);
 }
 if (!folio)
  return ERR_PTR(-ENOMEM);

allocated:
 __folio_set_locked(folio);
 __folio_set_swapbacked(folio);

 gfp &= GFP_RECLAIM_MASK;
 error = mem_cgroup_charge(folio, fault_mm, gfp);
 if (error) {
  if (xa_find(&mapping->i_pages, &index,
    index + pages - 1, XA_PRESENT)) {
   error = -EEXIST;
  } else if (pages > 1) {
   if (pages == HPAGE_PMD_NR) {
    count_vm_event(THP_FILE_FALLBACK);
    count_vm_event(THP_FILE_FALLBACK_CHARGE);
   }
   count_mthp_stat(folio_order(folio), MTHP_STAT_SHMEM_FALLBACK);
   count_mthp_stat(folio_order(folio), MTHP_STAT_SHMEM_FALLBACK_CHARGE);
  }
  goto unlock;
 }

 error = shmem_add_to_page_cache(folio, mapping, index, NULL, gfp);
 if (error)
  goto unlock;

 error = shmem_inode_acct_blocks(inode, pages);
 if (error) {
  struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
  long freed;
  /*
 * Try to reclaim some space by splitting a few
 * large folios beyond i_size on the filesystem.
 */

  shmem_unused_huge_shrink(sbinfo, NULL, pages);
  /*
 * And do a shmem_recalc_inode() to account for freed pages:
 * except our folio is there in cache, so not quite balanced.
 */

  spin_lock(&info->lock);
  freed = pages + info->alloced - info->swapped -
   READ_ONCE(mapping->nrpages);
  if (freed > 0)
   info->alloced -= freed;
  spin_unlock(&info->lock);
  if (freed > 0)
   shmem_inode_unacct_blocks(inode, freed);
  error = shmem_inode_acct_blocks(inode, pages);
  if (error) {
   filemap_remove_folio(folio);
   goto unlock;
  }
 }

 shmem_recalc_inode(inode, pages, 0);
 folio_add_lru(folio);
 return folio;

unlock:
 folio_unlock(folio);
 folio_put(folio);
 return ERR_PTR(error);
}

static struct folio *shmem_swap_alloc_folio(struct inode *inode,
  struct vm_area_struct *vma, pgoff_t index,
  swp_entry_t entry, int order, gfp_t gfp)
{
 struct shmem_inode_info *info = SHMEM_I(inode);
 int nr_pages = 1 << order;
 struct folio *new;
 gfp_t alloc_gfp;
 void *shadow;

 /*
 * We have arrived here because our zones are constrained, so don't
 * limit chance of success with further cpuset and node constraints.
 */

 gfp &= ~GFP_CONSTRAINT_MASK;
 alloc_gfp = gfp;
 if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
  if (WARN_ON_ONCE(order))
   return ERR_PTR(-EINVAL);
 } else if (order) {
  /*
 * If uffd is active for the vma, we need per-page fault
 * fidelity to maintain the uffd semantics, then fallback
 * to swapin order-0 folio, as well as for zswap case.
 * Any existing sub folio in the swap cache also blocks
 * mTHP swapin.
 */

  if ((vma && unlikely(userfaultfd_armed(vma))) ||
       !zswap_never_enabled() ||
       non_swapcache_batch(entry, nr_pages) != nr_pages)
   goto fallback;

  alloc_gfp = limit_gfp_mask(vma_thp_gfp_mask(vma), gfp);
 }
retry:
 new = shmem_alloc_folio(alloc_gfp, order, info, index);
 if (!new) {
  new = ERR_PTR(-ENOMEM);
  goto fallback;
 }

 if (mem_cgroup_swapin_charge_folio(new, vma ? vma->vm_mm : NULL,
        alloc_gfp, entry)) {
  folio_put(new);
  new = ERR_PTR(-ENOMEM);
  goto fallback;
 }

 /*
 * Prevent parallel swapin from proceeding with the swap cache flag.
 *
 * Of course there is another possible concurrent scenario as well,
 * that is to say, the swap cache flag of a large folio has already
 * been set by swapcache_prepare(), while another thread may have
 * already split the large swap entry stored in the shmem mapping.
 * In this case, shmem_add_to_page_cache() will help identify the
 * concurrent swapin and return -EEXIST.
 */

 if (swapcache_prepare(entry, nr_pages)) {
  folio_put(new);
  new = ERR_PTR(-EEXIST);
  /* Try smaller folio to avoid cache conflict */
  goto fallback;
 }

 __folio_set_locked(new);
 __folio_set_swapbacked(new);
 new->swap = entry;

 memcg1_swapin(entry, nr_pages);
 shadow = get_shadow_from_swap_cache(entry);
 if (shadow)
  workingset_refault(new, shadow);
 folio_add_lru(new);
 swap_read_folio(new, NULL);
 return new;
fallback:
 /* Order 0 swapin failed, nothing to fallback to, abort */
 if (!order)
  return new;
 entry.val += index - round_down(index, nr_pages);
 alloc_gfp = gfp;
 nr_pages = 1;
 order = 0;
 goto retry;
}

/*
 * When a page is moved from swapcache to shmem filecache (either by the
 * usual swapin of shmem_get_folio_gfp(), or by the less common swapoff of
 * shmem_unuse_inode()), it may have been read in earlier from swap, in
 * ignorance of the mapping it belongs to.  If that mapping has special
 * constraints (like the gma500 GEM driver, which requires RAM below 4GB),
 * we may need to copy to a suitable page before moving to filecache.
 *
 * In a future release, this may well be extended to respect cpuset and
 * NUMA mempolicy, and applied also to anonymous pages in do_swap_page();
 * but for now it is a simple matter of zone.
 */

static bool shmem_should_replace_folio(struct folio *folio, gfp_t gfp)
{
 return folio_zonenum(folio) > gfp_zone(gfp);
}

static int shmem_replace_folio(struct folio **foliop, gfp_t gfp,
    struct shmem_inode_info *info, pgoff_t index,
    struct vm_area_struct *vma)
{
 struct folio *new, *old = *foliop;
 swp_entry_t entry = old->swap;
 struct address_space *swap_mapping = swap_address_space(entry);
 pgoff_t swap_index = swap_cache_index(entry);
 XA_STATE(xas, &swap_mapping->i_pages, swap_index);
 int nr_pages = folio_nr_pages(old);
 int error = 0, i;

 /*
 * We have arrived here because our zones are constrained, so don't
 * limit chance of success by further cpuset and node constraints.
 */

 gfp &= ~GFP_CONSTRAINT_MASK;
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 if (nr_pages > 1) {
  gfp_t huge_gfp = vma_thp_gfp_mask(vma);

  gfp = limit_gfp_mask(huge_gfp, gfp);
 }
#endif

 new = shmem_alloc_folio(gfp, folio_order(old), info, index);
 if (!new)
  return -ENOMEM;

 folio_ref_add(new, nr_pages);
 folio_copy(new, old);
 flush_dcache_folio(new);

 __folio_set_locked(new);
 __folio_set_swapbacked(new);
 folio_mark_uptodate(new);
 new->swap = entry;
 folio_set_swapcache(new);

 /* Swap cache still stores N entries instead of a high-order entry */
 xa_lock_irq(&swap_mapping->i_pages);
 for (i = 0; i < nr_pages; i++) {
  void *item = xas_load(&xas);

  if (item != old) {
   error = -ENOENT;
   break;
  }

  xas_store(&xas, new);
  xas_next(&xas);
 }
 if (!error) {
  mem_cgroup_replace_folio(old, new);
  shmem_update_stats(new, nr_pages);
  shmem_update_stats(old, -nr_pages);
 }
 xa_unlock_irq(&swap_mapping->i_pages);

 if (unlikely(error)) {
  /*
 * Is this possible?  I think not, now that our callers
 * check both the swapcache flag and folio->private
 * after getting the folio lock; but be defensive.
 * Reverse old to newpage for clear and free.
 */

  old = new;
 } else {
  folio_add_lru(new);
  *foliop = new;
 }

 folio_clear_swapcache(old);
 old->private = NULL;

 folio_unlock(old);
 /*
 * The old folio are removed from swap cache, drop the 'nr_pages'
 * reference, as well as one temporary reference getting from swap
 * cache.
 */

 folio_put_refs(old, nr_pages + 1);
 return error;
}

static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
      struct folio *folio, swp_entry_t swap,
      bool skip_swapcache)
{
 struct address_space *mapping = inode->i_mapping;
 swp_entry_t swapin_error;
 void *old;
 int nr_pages;

 swapin_error = make_poisoned_swp_entry();
 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;

 nr_pages = folio_nr_pages(folio);
 folio_wait_writeback(folio);
 if (!skip_swapcache)
  delete_from_swap_cache(folio);
 /*
 * 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(i_blocks)
 * in shmem_evict_inode().
 */

 shmem_recalc_inode(inode, -nr_pages, -nr_pages);
 swap_free_nr(swap, nr_pages);
}

static int shmem_split_large_entry(struct inode *inode, pgoff_t index,
       swp_entry_t swap, gfp_t gfp)
{
 struct address_space *mapping = inode->i_mapping;
 XA_STATE_ORDER(xas, &mapping->i_pages, index, 0);
 int split_order = 0, entry_order;
 int i;

 /* Convert user data gfp flags to xarray node gfp flags */
 gfp &= GFP_RECLAIM_MASK;

 for (;;) {
  void *old = NULL;
  int cur_order;
  pgoff_t swap_index;

  xas_lock_irq(&xas);
  old = xas_load(&xas);
  if (!xa_is_value(old) || swp_to_radix_entry(swap) != old) {
   xas_set_err(&xas, -EEXIST);
   goto unlock;
  }

  entry_order = xas_get_order(&xas);

  if (!entry_order)
   goto unlock;

  /* Try to split large swap entry in pagecache */
  cur_order = entry_order;
  swap_index = round_down(index, 1 << entry_order);

  split_order = xas_try_split_min_order(cur_order);

  while (cur_order > 0) {
   pgoff_t aligned_index =
    round_down(index, 1 << cur_order);
   pgoff_t swap_offset = aligned_index - swap_index;

   xas_set_order(&xas, index, split_order);
   xas_try_split(&xas, old, cur_order);
   if (xas_error(&xas))
    goto unlock;

   /*
 * Re-set the swap entry after splitting, and the swap
 * offset of the original large entry must be continuous.
 */

   for (i = 0; i < 1 << cur_order;
        i += (1 << split_order)) {
    swp_entry_t tmp;

    tmp = swp_entry(swp_type(swap),
      swp_offset(swap) + swap_offset +
       i);
    __xa_store(&mapping->i_pages, aligned_index + i,
        swp_to_radix_entry(tmp), 0);
   }
   cur_order = split_order;
   split_order = xas_try_split_min_order(split_order);
  }

unlock:
  xas_unlock_irq(&xas);

  if (!xas_nomem(&xas, gfp))
   break;
 }

 if (xas_error(&xas))
  return xas_error(&xas);

 return 0;
}

/*
 * Swap in the folio pointed to by *foliop.
 * Caller has to make sure that *foliop contains a valid swapped folio.
 * Returns 0 and the folio in foliop if success. On failure, returns the
 * error code and NULL in *foliop.
 */

static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
        struct folio **foliop, enum sgp_type sgp,
        gfp_t gfp, struct vm_area_struct *vma,
        vm_fault_t *fault_type)
{
 struct address_space *mapping = inode->i_mapping;
 struct mm_struct *fault_mm = vma ? vma->vm_mm : NULL;
 struct shmem_inode_info *info = SHMEM_I(inode);
 swp_entry_t swap, index_entry;
 struct swap_info_struct *si;
 struct folio *folio = NULL;
 bool skip_swapcache = false;
 int error, nr_pages, order;
 pgoff_t offset;

 VM_BUG_ON(!*foliop || !xa_is_value(*foliop));
 index_entry = radix_to_swp_entry(*foliop);
 swap = index_entry;
 *foliop = NULL;

 if (is_poisoned_swp_entry(index_entry))
  return -EIO;

 si = get_swap_device(index_entry);
 order = shmem_confirm_swap(mapping, index, index_entry);
 if (unlikely(!si)) {
  if (order < 0)
   return -EEXIST;
  else
   return -EINVAL;
 }
 if (unlikely(order < 0)) {
  put_swap_device(si);
  return -EEXIST;
 }

 /* index may point to the middle of a large entry, get the sub entry */
 if (order) {
  offset = index - round_down(index, 1 << order);
  swap = swp_entry(swp_type(swap), swp_offset(swap) + offset);
 }

 /* Look it up and read it in.. */
 folio = swap_cache_get_folio(swap, NULL, 0);
 if (!folio) {
  if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) {
   /* Direct swapin skipping swap cache & readahead */
   folio = shmem_swap_alloc_folio(inode, vma, index,
             index_entry, order, gfp);
   if (IS_ERR(folio)) {
    error = PTR_ERR(folio);
    folio = NULL;
    goto failed;
   }
   skip_swapcache = true;
  } else {
   /* Cached swapin only supports order 0 folio */
   folio = shmem_swapin_cluster(swap, gfp, info, index);
   if (!folio) {
    error = -ENOMEM;
    goto failed;
   }
  }
  if (fault_type) {
   *fault_type |= VM_FAULT_MAJOR;
   count_vm_event(PGMAJFAULT);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=89 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge