cregit-Linux how code gets into the kernel

Release 4.8 mm/shmem.c

Directory: mm
/*
 * 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/mm.h>
#include <linux/export.h>
#include <linux/swap.h>
#include <linux/uio.h>
#include <linux/khugepaged.h>


static struct vfsmount *shm_mnt;

#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/shmem_fs.h>
#include <linux/writeback.h>
#include <linux/blkdev.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 <asm/uaccess.h>
#include <asm/pgtable.h>

#include "internal.h"


#define BLOCKS_PER_PAGE  (PAGE_SIZE/512)

#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

/* 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_writepage via
 * inode->i_private (with i_mutex 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 writepage refused to swap out */
};

#ifdef CONFIG_TMPFS

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

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton14100.00%1100.00%
Total14100.00%1100.00%


static unsigned long shmem_default_max_inodes(void) { return min(totalram_pages - totalhigh_pages, totalram_pages / 2); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton21100.00%1100.00%
Total21100.00%1100.00%

#endif static bool shmem_should_replace_page(struct page *page, gfp_t gfp); static int shmem_replace_page(struct page **pagep, gfp_t gfp, struct shmem_inode_info *info, pgoff_t index); static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, struct page **pagep, enum sgp_type sgp, gfp_t gfp, struct mm_struct *fault_mm, int *fault_type);
int shmem_getpage(struct inode *inode, pgoff_t index, struct page **pagep, enum sgp_type sgp) { return shmem_getpage_gfp(inode, index, pagep, sgp, mapping_gfp_mask(inode->i_mapping), NULL, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins4393.48%150.00%
andres lagar-cavillaandres lagar-cavilla36.52%150.00%
Total46100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
brian gerstbrian gerst1578.95%266.67%
linus torvaldslinus torvalds421.05%133.33%
Total19100.00%3100.00%

/* * 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)); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2777.14%133.33%
al viroal viro514.29%133.33%
hugh dickinshugh dickins38.57%133.33%
Total35100.00%3100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2787.10%150.00%
hugh dickinshugh dickins412.90%150.00%
Total31100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
konstantin khlebnikovkonstantin khlebnikov87100.00%1100.00%
Total87100.00%1100.00%

/* * ... whereas tmpfs objects are accounted incrementally as * pages are allocated, in order to allow large sparse files. * shmem_getpage reports shmem_acct_block failure as -ENOSPC not -ENOMEM, * so that a failure on a sparse tmpfs mapping will give SIGBUS not OOM. */
static inline int shmem_acct_block(unsigned long flags, long pages) { if (!(flags & VM_NORESERVE)) return 0; return security_vm_enough_memory_mm(current->mm, pages * VM_ACCT(PAGE_SIZE)); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2151.22%120.00%
kirill a. shutemovkirill a. shutemov1434.15%240.00%
al viroal viro512.20%120.00%
hugh dickinshugh dickins12.44%120.00%
Total41100.00%5100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2893.33%133.33%
kirill a. shutemovkirill a. shutemov13.33%133.33%
hugh dickinshugh dickins13.33%133.33%
Total30100.00%3100.00%

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 struct file_system_type shmem_fs_type; static LIST_HEAD(shmem_swaplist); static DEFINE_MUTEX(shmem_swaplist_mutex);
static int shmem_reserve_inode(struct super_block *sb) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); if (sbinfo->max_inodes) { spin_lock(&sbinfo->stat_lock); if (!sbinfo->free_inodes) { spin_unlock(&sbinfo->stat_lock); return -ENOSPC; } sbinfo->free_inodes--; spin_unlock(&sbinfo->stat_lock); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pavel emelianovpavel emelianov74100.00%1100.00%
Total74100.00%1100.00%


static void shmem_free_inode(struct super_block *sb) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); if (sbinfo->max_inodes) { spin_lock(&sbinfo->stat_lock); sbinfo->free_inodes++; spin_unlock(&sbinfo->stat_lock); } }

Contributors

PersonTokensPropCommitsCommitProp
pavel emelianovpavel emelianov50100.00%1100.00%
Total50100.00%1100.00%

/** * shmem_recalc_inode - recalculate the block usage of an inode * @inode: inode to recalc * * 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) * * It has to be called with the spinlock held. */
static void shmem_recalc_inode(struct inode *inode) { struct shmem_inode_info *info = SHMEM_I(inode); long freed; freed = info->alloced - info->swapped - inode->i_mapping->nrpages; if (freed > 0) { struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); if (sbinfo->max_blocks) percpu_counter_add(&sbinfo->used_blocks, -freed); info->alloced -= freed; inode->i_blocks -= freed * BLOCKS_PER_PAGE; shmem_unacct_blocks(info->flags, freed); } }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins6767.00%375.00%
linus torvaldslinus torvalds3333.00%125.00%
Total100100.00%4100.00%


bool shmem_charge(struct inode *inode, long pages) { struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); unsigned long flags; if (shmem_acct_block(info->flags, pages)) return false; spin_lock_irqsave(&info->lock, flags); info->alloced += pages; inode->i_blocks += pages * BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock_irqrestore(&info->lock, flags); inode->i_mapping->nrpages += pages; if (!sbinfo->max_blocks) return true; if (percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks - pages) > 0) { inode->i_mapping->nrpages -= pages; spin_lock_irqsave(&info->lock, flags); info->alloced -= pages; shmem_recalc_inode(inode); spin_unlock_irqrestore(&info->lock, flags); shmem_unacct_blocks(info->flags, pages); return false; } percpu_counter_add(&sbinfo->used_blocks, pages); return true; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov14373.71%240.00%
hugh dickinshugh dickins4523.20%240.00%
johannes weinerjohannes weiner63.09%120.00%
Total194100.00%5100.00%


void shmem_uncharge(struct inode *inode, long pages) { struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); unsigned long flags; spin_lock_irqsave(&info->lock, flags); info->alloced -= pages; inode->i_blocks -= pages * BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock_irqrestore(&info->lock, flags); if (sbinfo->max_blocks) percpu_counter_sub(&sbinfo->used_blocks, pages); shmem_unacct_blocks(info->flags, pages); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov8380.58%250.00%
hugh dickinshugh dickins2019.42%250.00%
Total103100.00%4100.00%

/* * Replace item expected in radix tree by a new item, while holding tree lock. */
static int shmem_radix_tree_replace(struct address_space *mapping, pgoff_t index, void *expected, void *replacement) { void **pslot; void *item; VM_BUG_ON(!expected); VM_BUG_ON(!replacement); pslot = radix_tree_lookup_slot(&mapping->page_tree, index); if (!pslot) return -ENOENT; item = radix_tree_deref_slot_protected(pslot, &mapping->tree_lock); if (item != expected) return -ENOENT; radix_tree_replace_slot(pslot, replacement); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov96100.00%1100.00%
Total96100.00%1100.00%

/* * Sometimes, before we decide whether to proceed or to fail, we must check * that an entry was not already brought back from swap by a racing thread. * * Checking page is not enough: by the time a SwapCache page is locked, it * might be reused, and again be SwapCache, using the same swap as before. */
static bool shmem_confirm_swap(struct address_space *mapping, pgoff_t index, swp_entry_t swap) { void *item; rcu_read_lock(); item = radix_tree_lookup(&mapping->page_tree, index); rcu_read_unlock(); return item == swp_to_radix_entry(swap); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov3778.72%150.00%
hugh dickinshugh dickins1021.28%150.00%
Total47100.00%2100.00%

/* * 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 fadvise()/madvise() hints; * SHMEM_HUGE_ADVISE: * only allocate huge pages if requested with fadvise()/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_HUGE_PAGECACHE /* ifdef here to avoid bloating shmem.o when not necessary */ int shmem_huge __read_mostly;
static int shmem_parse_huge(const char *str) { if (!strcmp(str, "never")) return SHMEM_HUGE_NEVER; if (!strcmp(str, "always")) return SHMEM_HUGE_ALWAYS; if (!strcmp(str, "within_size")) return SHMEM_HUGE_WITHIN_SIZE; if (!strcmp(str, "advise")) return SHMEM_HUGE_ADVISE; if (!strcmp(str, "deny")) return SHMEM_HUGE_DENY; if (!strcmp(str, "force")) return SHMEM_HUGE_FORCE; return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov93100.00%1100.00%
Total93100.00%1100.00%


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"; } }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov62100.00%1100.00%
Total62100.00%1100.00%


static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo, struct shrink_control *sc, unsigned long nr_to_split) { LIST_HEAD(list), *pos, *next; struct inode *inode; struct shmem_inode_info *info; struct page *page; unsigned long batch = sc ? sc->nr_to_scan : 128; int removed = 0, split = 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); removed++; goto next; } /* Check if there's anything to gain */ if (round_up(inode->i_size, PAGE_SIZE) == round_up(inode->i_size, HPAGE_PMD_SIZE)) { list_del_init(&info->shrinklist); removed++; iput(inode); goto next; } list_move(&info->shrinklist, &list); next: if (!--batch) break; } spin_unlock(&sbinfo->shrinklist_lock); list_for_each_safe(pos, next, &list) { int ret; info = list_entry(pos, struct shmem_inode_info, shrinklist); inode = &info->vfs_inode; if (nr_to_split && split >= nr_to_split) { iput(inode); continue; } page = find_lock_page(inode->i_mapping, (inode->i_size & HPAGE_PMD_MASK) >> PAGE_SHIFT); if (!page) goto drop; if (!PageTransHuge(page)) { unlock_page(page); put_page(page); goto drop; } ret = split_huge_page(page); unlock_page(page); put_page(page); if (ret) { /* split failed: leave it on the list */ iput(inode); continue; } split++; drop: list_del_init(&info->shrinklist); removed++; iput(inode); } spin_lock(&sbinfo->shrinklist_lock); list_splice_tail(&list, &sbinfo->shrinklist); sbinfo->shrinklist_len -= removed; spin_unlock(&sbinfo->shrinklist_lock); return split; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov399100.00%1100.00%
Total399100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov49100.00%1100.00%
Total49100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov34100.00%1100.00%
Total34100.00%1100.00%

#else /* !CONFIG_TRANSPARENT_HUGE_PAGECACHE */ #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_split) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov24100.00%1100.00%
Total24100.00%1100.00%

#endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE */ /* * Like add_to_page_cache_locked, but error if expected item has gone. */
static int shmem_add_to_page_cache(struct page *page, struct address_space *mapping, pgoff_t index, void *expected) { int error, nr = hpage_nr_pages(page); VM_BUG_ON_PAGE(PageTail(page), page); VM_BUG_ON_PAGE(index != round_down(index, nr), page); VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(!PageSwapBacked(page), page); VM_BUG_ON(expected && PageTransHuge(page)); page_ref_add(page, nr); page->mapping = mapping; page->index = index; spin_lock_irq(&mapping->tree_lock); if (PageTransHuge(page)) { void __rcu **results; pgoff_t idx; int i; error = 0; if (radix_tree_gang_lookup_slot(&mapping->page_tree, &results, &idx, index, 1) && idx < index + HPAGE_PMD_NR) { error = -EEXIST; } if (!error) { for (i = 0; i < HPAGE_PMD_NR; i++) { error = radix_tree_insert(&mapping->page_tree, index + i, page + i); VM_BUG_ON(error); } count_vm_event(THP_FILE_ALLOC); } } else if (!expected) { error = radix_tree_insert(&mapping->page_tree, index, page); } else { error = shmem_radix_tree_replace(mapping, index, expected, page); } if (!error) { mapping->nrpages += nr; if (PageTransHuge(page)) __inc_node_page_state(page, NR_SHMEM_THPS); __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, nr); __mod_node_page_state(page_pgdat(page), NR_SHMEM, nr); spin_unlock_irq(&mapping->tree_lock); } else { page->mapping = NULL; spin_unlock_irq(&mapping->tree_lock); page_ref_sub(page, nr); } return error; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov20258.05%125.00%
hugh dickinshugh dickins13538.79%125.00%
sasha levinsasha levin61.72%125.00%
mel gormanmel gorman51.44%125.00%
Total348100.00%4100.00%

/* * Like delete_from_page_cache, but substitutes swap for page. */
static void shmem_delete_from_page_cache(struct page *page, void *radswap) { struct address_space *mapping = page->mapping; int error; VM_BUG_ON_PAGE(PageCompound(page), page); spin_lock_irq(&mapping->tree_lock); error = shmem_radix_tree_replace(mapping, page->index, page, radswap); page->mapping = NULL; mapping->nrpages--; __dec_node_page_state(page, NR_FILE_PAGES); __dec_node_page_state(page, NR_SHMEM); spin_unlock_irq(&mapping->tree_lock); put_page(page); BUG_ON(error); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins9087.38%125.00%
kirill a. shutemovkirill a. shutemov1110.68%250.00%
mel gormanmel gorman21.94%125.00%
Total103100.00%4100.00%

/* * Remove swap entry from radix tree, free the swap and its page cache. */
static int shmem_free_swap(struct address_space *mapping, pgoff_t index, void *radswap) { void *old; spin_lock_irq(&mapping->tree_lock); old = radix_tree_delete_item(&mapping->page_tree, index, radswap); spin_unlock_irq(&mapping->tree_lock); if (old != radswap) return -ENOENT; free_swap_and_cache(radix_to_swp_entry(radswap)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins5778.08%150.00%
johannes weinerjohannes weiner1621.92%150.00%
Total73100.00%2100.00%

/* * 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_mutex or mapping->tree_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) { struct radix_tree_iter iter; void **slot; struct page *page; unsigned long swapped = 0; rcu_read_lock(); radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) { if (iter.index >= end) break; page = radix_tree_deref_slot(slot); if (radix_tree_deref_retry(page)) { slot = radix_tree_iter_retry(&iter); continue; } if (radix_tree_exceptional_entry(page)) swapped++; if (need_resched()) { cond_resched_rcu(); slot = radix_tree_iter_next(&iter); } } rcu_read_unlock(); return swapped << PAGE_SHIFT; }

Contributors

PersonTokensPropCommitsCommitProp
vlastimil babkavlastimil babka10686.89%250.00%
matthew wilcoxmatthew wilcox1613.11%250.00%
Total122100.00%4100.00%

/* * 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_mutex or mapping->tree_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, linear_page_index(vma, vma->vm_start), linear_page_index(vma, vma->vm_end)); }

Contributors

PersonTokensPropCommitsCommitProp
vlastimil babkavlastimil babka114100.00%1100.00%
Total114100.00%1100.00%

/* * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists. */
void shmem_unlock_mapping(struct address_space *mapping) { struct pagevec pvec; pgoff_t indices[PAGEVEC_SIZE]; pgoff_t index = 0; pagevec_init(&pvec, 0); /* * Minor point, but we might as well stop if someone else SHM_LOCKs it. */ while (!mapping_unevictable(mapping)) { /* * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it * has finished, if it hits a row of PAGEVEC_SIZE swap entries. */ pvec.nr = find_get_entries(mapping, index, PAGEVEC_SIZE, pvec.pages, indices); if (!pvec.nr) break; index = indices[pvec.nr - 1] + 1; pagevec_remove_exceptionals(&pvec); check_move_unevictable_pages(pvec.pages, pvec.nr); pagevec_release(&pvec); cond_resched(); } }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins10998.20%266.67%
johannes weinerjohannes weiner21.80%133.33%
Total111100.00%3100.00%

/* * Remove range of pages and swap entries from radix tree, 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; unsigned int partial_start = lstart & (PAGE_SIZE - 1); unsigned int partial_end = (lend + 1) & (PAGE_SIZE - 1); struct pagevec pvec; pgoff_t indices[PAGEVEC_SIZE]; long nr_swaps_freed = 0; pgoff_t index; int i; if (lend == -1) end = -1; /* unsigned, so actually very big */ pagevec_init(&pvec, 0); index = start; while (index < end) { pvec.nr = find_get_entries(mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE), pvec.pages, indices); if (!pvec.nr) break; for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; index = indices[i]; if (index >= end) break; if (radix_tree_exceptional_entry(page)) { if (unfalloc) continue; nr_swaps_freed += !shmem_free_swap(mapping, index, page); continue; } VM_BUG_ON_PAGE(page_to_pgoff(page) != index, page); if (!trylock_page(page)) continue; if (PageTransTail(page)) { /* Middle of THP: zero out the page */ clear_highpage(page); unlock_page(page); continue; } else if (PageTransHuge(page)) { if (index == round_down(end, HPAGE_PMD_NR)) { /* * Range ends in the middle of THP: * zero out the page */ clear_highpage(page); unlock_page(page); continue; } index += HPAGE_PMD_NR - 1; i += HPAGE_PMD_NR - 1; } if (!unfalloc || !PageUptodate(page)) { VM_BUG_ON_PAGE(PageTail(page), page); if (page_mapping(page) == mapping) { VM_BUG_ON_PAGE(PageWriteback(page), page); truncate_inode_page(mapping, page); } } unlock_page(page); } pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); cond_resched(); index++; } if (partial_start) { struct page *page = NULL; shmem_getpage(inode, start - 1, &page, SGP_READ); if (page) { unsigned int top = PAGE_SIZE; if (start > end) { top = partial_end; partial_end = 0; } zero_user_segment(page, partial_start, top); set_page_dirty(page); unlock_page(page); put_page(page); } } if (partial_end) { struct page *page = NULL; shmem_getpage(inode, end, &page, SGP_READ); if (page) { zero_user_segment(page, 0, partial_end); set_page_dirty(page); unlock_page(page); put_page(page); } } if (start >= end) return; index = start; while (index < end) { cond_resched(); pvec.nr = find_get_entries(mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE), pvec.pages, indices); if (!pvec.nr) { /* 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 < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; index = indices[i]; if (index >= end) break; if (radix_tree_exceptional_entry(page)) { if (unfalloc) continue; if (shmem_free_swap(mapping, index, page)) { /* Swap was replaced by page: retry */ index--; break; } nr_swaps_freed++; continue; } lock_page(page); if (PageTransTail(page)) { /* Middle of THP: zero out the page */ clear_highpage(page); unlock_page(page); /* * Partial thp truncate due 'start' in middle * of THP: don't need to look on these pages * again on !pvec.nr restart. */ if (index != round_down(end, HPAGE_PMD_NR)) start++; continue; } else if (PageTransHuge(page)) { if (index == round_down(end, HPAGE_PMD_NR)) { /* * Range ends in the middle of THP: * zero out the page */ clear_highpage(page); unlock_page(page); continue; } index += HPAGE_PMD_NR - 1; i += HPAGE_PMD_NR - 1; } if (!unfalloc || !PageUptodate(page)) { VM_BUG_ON_PAGE(PageTail(page), page); if (page_mapping(page) == mapping) { VM_BUG_ON_PAGE(PageWriteback(page), page); truncate_inode_page(mapping, page); } else { /* Page was replaced by swap: retry */ unlock_page(page); index--; break; } } unlock_page(page); } pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); index++; } spin_lock_irq(&info->lock); info->swapped -= nr_swaps_freed; shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins68174.84%1254.55%
kirill a. shutemovkirill a. shutemov20422.42%313.64%
pre-gitpre-git80.88%14.55%
sasha levinsasha levin60.66%14.55%
linus torvaldslinus torvalds50.55%29.09%
johannes weinerjohannes weiner40.44%14.55%
andrew mortonandrew morton20.22%29.09%
Total910100.00%22100.00%


void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) { shmem_undo_range(inode, lstart, lend, false); inode->i_ctime = inode->i_mtime = CURRENT_TIME; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins37100.00%3100.00%
Total37100.00%3100.00%

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_irq(&info->lock); shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); } generic_fillattr(inode, stat); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
yu zhaoyu zhao6977.53%133.33%
hugh dickinshugh dickins1820.22%133.33%
kirill a. shutemovkirill a. shutemov22.25%133.33%
Total89100.00%3100.00%


static int shmem_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); int error; error = inode_change_ok(inode, attr); if (error) return error; 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_mutex */ 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); inode->i_ctime = inode->i_mtime = CURRENT_TIME; } 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); /* * Part of the huge page can be beyond i_size: subject * to shrink under memory pressure. */ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) { spin_lock(&sbinfo->shrinklist_lock); if (list_empty(&info->shrinklist)) { list_add_tail(&info->shrinklist, &sbinfo->shrinklist); sbinfo->shrinklist_len++; } spin_unlock(&sbinfo->shrinklist_lock); } } } setattr_copy(inode, attr); if (attr->ia_valid & ATTR_MODE) error = posix_acl_chmod(inode, inode->i_mode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins9026.16%320.00%
kirill a. shutemovkirill a. shutemov6819.77%16.67%
david herrmanndavid herrmann4513.08%16.67%
andrew mortonandrew morton4212.21%16.67%
nick pigginnick piggin277.85%213.33%
andreas gruenbacherandreas gruenbacher236.69%16.67%
konstantin khlebnikovkonstantin khlebnikov236.69%16.67%
christoph hellwigchristoph hellwig226.40%320.00%
david howellsdavid howells30.87%16.67%
josef bacikjosef bacik10.29%16.67%
Total344100.00%15100.00%


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); if (inode->i_mapping->a_ops == &shmem_aops) { shmem_unacct_size(info->flags, inode->i_size); inode->i_size = 0; 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); } if (!list_empty(&info->swaplist)) { mutex_lock(&shmem_swaplist_mutex); list_del_init(&info->swaplist); mutex_unlock(&shmem_swaplist_mutex); } } simple_xattrs_free(&info->xattrs); WARN_ON(inode->i_blocks); shmem_free_inode(inode->i_sb); clear_inode(inode); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins9851.31%753.85%
kirill a. shutemovkirill a. shutemov6735.08%17.69%
linus torvaldslinus torvalds126.28%17.69%
pre-gitpre-git84.19%17.69%
aristeu sergio rozanski filhoaristeu sergio rozanski filho42.09%17.69%
al viroal viro10.52%17.69%
jan karajan kara10.52%17.69%
Total191100.00%13100.00%

/* * If swap found in inode, free it and move page from swapcache to filecache. */
static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t swap, struct page **pagep) { struct address_space *mapping = info->vfs_inode.i_mapping; void *radswap; pgoff_t index; gfp_t gfp; int error = 0; radswap = swp_to_radix_entry(swap); index = radix_tree_locate_item(&mapping->page_tree, radswap); if (index == -1) return -EAGAIN; /* tell shmem_unuse we found nothing */ /* * Move _head_ to start search for next from here. * But be careful: shmem_evict_inode checks list_empty without taking * mutex, and there's an instant in list_move_tail when info->swaplist * would appear empty, if it were the only one on shmem_swaplist. */ if (shmem_swaplist.next != &info->swaplist) list_move_tail(&shmem_swaplist, &info->swaplist); gfp = mapping_gfp_mask(mapping); if (shmem_should_replace_page(*pagep, gfp)) { mutex_unlock(&shmem_swaplist_mutex); error = shmem_replace_page(pagep, gfp, info, index); mutex_lock(&shmem_swaplist_mutex); /* * We needed to drop mutex to make that restrictive page * allocation, but the inode might have been freed while we * dropped it: although a racing shmem_evict_inode() cannot * complete without emptying the radix_tree, our page lock * on this swapcache page is not enough to prevent that - * free_swap_and_cache() of our swap entry will only * trylock_page(), removing swap from radix_tree whatever. * * We must not proceed to shmem_add_to_page_cache() if the * inode has been freed, but of course we cannot rely on * inode or mapping or info to check that. However, we can * safely check if our swap entry is still in use (and here * it can't have got reused for another page): if it's still * in use, then the inode cannot have been freed yet, and we * can safely proceed (if it's no longer in use, that tells * nothing about the inode, but we don't need to unuse swap). */ if (!page_swapcount(*pagep)) error = -ENOENT; } /* * We rely on shmem_swaplist_mutex, not only to protect the swaplist, * but also to hold up shmem_evict_inode(): so inode cannot be freed * beneath us (pagelock doesn't help until the page is in pagecache). */ if (!error) error = shmem_add_to_page_cache(*pagep, mapping, index, radswap); if (error != -ENOMEM) { /* * Truncation and eviction use free_swap_and_cache(), which * only does trylock page: if we raced, best clean up here. */ delete_from_swap_cache(*pagep); set_page_dirty(*pagep); if (!error) { spin_lock_irq(&info->lock); info->swapped--; spin_unlock_irq(&info->lock); swap_free(swap); } } return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins22394.09%1773.91%
linus torvaldslinus torvalds62.53%28.70%
johannes weinerjohannes weiner31.27%14.35%
andrew mortonandrew morton31.27%28.70%
kirill a. shutemovkirill a. shutemov20.84%14.35%
Total237100.00%23100.00%

/* * Search through swapped inodes to find and replace swap by page. */
int shmem_unuse(swp_entry_t swap, struct page *page) { struct list_head *this, *next; struct shmem_inode_info *info; struct mem_cgroup *memcg; int error = 0; /* * There's a faint possibility that swap page was replaced before * caller locked it: caller will come back later with the right page. */ if (unlikely(!PageSwapCache(page) || page_private(page) != swap.val)) goto out; /* * Charge page using GFP_KERNEL while we can wait, before taking * the shmem_swaplist_mutex which might hold up shmem_writepage(). * Charged back to the user (not to caller) when swap account is used. */ error = mem_cgroup_try_charge(page, current->mm, GFP_KERNEL, &memcg, false); if (error) goto out; /* No radix_tree_preload: swap entry keeps a place for page in tree */ error = -EAGAIN; mutex_lock(&shmem_swaplist_mutex); list_for_each_safe(this, next, &shmem_swaplist) { info = list_entry(this, struct shmem_inode_info, swaplist); if (info->swapped) error = shmem_unuse_inode(info, swap, &page); else list_del_init(&info->swaplist); cond_resched(); if (error != -EAGAIN) break; /* found nothing in this: move on to search the next */ } mutex_unlock(&shmem_swaplist_mutex); if (error) { if (error != -ENOMEM) error = 0; mem_cgroup_cancel_charge(page, memcg, false); } else mem_cgroup_commit_charge(page, memcg, true, false); out: unlock_page(page); put_page(page); return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins12155.50%1365.00%
johannes weinerjohannes weiner4721.56%15.00%
linus torvaldslinus torvalds3716.97%315.00%
kirill a. shutemovkirill a. shutemov73.21%210.00%
pre-gitpre-git62.75%15.00%
Total218100.00%20100.00%

/* * Move the page from the page cache to the swap cache. */
static int shmem_writepage(struct page *page, struct writeback_control *wbc) { struct shmem_inode_info *info; struct address_space *mapping; struct inode *inode; swp_entry_t swap; pgoff_t index; VM_BUG_ON_PAGE(PageCompound(page), page); BUG_ON(!PageLocked(page)); mapping = page->mapping; index = page->index; inode = mapping->host; info = SHMEM_I(inode); if (info->flags & VM_LOCKED) goto redirty; if (!total_swap_pages) goto redirty; /* * Our capabilities prevent regular writeback or sync from ever calling * shmem_writepage; but a stacking filesystem might use ->writepage of * its underlying filesystem, in which case tmpfs should write out to * swap only in response to memory pressure, and not for the writeback * threads or sync. */ if (!wbc->for_reclaim) { WARN_ON_ONCE(1); /* Still happens? Tell us about it! */ goto redirty; } /* * 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 page arriving here is now to initialize it and write it. * * That's okay for a page already fallocated earlier, but if we have * not yet completed the fallocation, then (a) we want to keep track * of this page 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 page, and let shmem_fallocate() quit when too many. */ if (!PageUptodate(page)) { 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++; else shmem_falloc = NULL; spin_unlock(&inode->i_lock); if (shmem_falloc) goto redirty; } clear_highpage(page); flush_dcache_page(page); SetPageUptodate(page); } swap = get_swap_page(); if (!swap.val) goto redirty; if (mem_cgroup_try_charge_swap(page, swap)) goto free_swap; /* * Add inode to shmem_unuse()'s list of swapped-out inodes, * if it's not already there. Do it now before the page is * moved to swap cache, when its pagelock no longer protects * the inode from eviction. But don't unlock the mutex until * we've incremented swapped, because shmem_unuse_inode() will * prune a !swapped inode from the swaplist under this mutex. */ mutex_lock(&shmem_swaplist_mutex); if (list_empty(&info->swaplist)) list_add_tail(&info->swaplist, &shmem_swaplist); if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) { spin_lock_irq(&info->lock); shmem_recalc_inode(inode); info->swapped++; spin_unlock_irq(&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); return 0; } mutex_unlock(&shmem_swaplist_mutex); free_swap: swapcache_free(swap); redirty: set_page_dirty(page); if (wbc->for_reclaim) return AOP_WRITEPAGE_ACTIVATE; /* Return with page locked */ unlock_page(page); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins26166.75%2058.82%
linus torvaldslinus torvalds7118.16%411.76%
andrew mortonandrew morton256.39%38.82%
vladimir davydovvladimir davydov143.58%12.94%
kirill a. shutemovkirill a. shutemov123.07%25.88%
pre-gitpre-git51.28%12.94%
kamezawa hiroyukikamezawa hiroyuki10.26%12.94%
zach brownzach brown10.26%12.94%
christoph hellwigchristoph hellwig10.26%12.94%
Total391100.00%34100.00%

#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); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton3257.14%133.33%
lee schermerhornlee schermerhorn2442.86%266.67%
Total56100.00%3100.00%


static struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo) { struct mempolicy *mpol = NULL; if (sbinfo->mpol) { spin_lock(&sbinfo->stat_lock); /* prevent replace/use races */ mpol = sbinfo->mpol; mpol_get(mpol); spin_unlock(&sbinfo->stat_lock); } return mpol; }

Contributors

PersonTokensPropCommitsCommitProp
lee schermerhornlee schermerhorn59100.00%1100.00%
Total59100.00%1100.00%

#else /* !CONFIG_NUMA || !CONFIG_TMPFS */
static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol) { }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins16100.00%1100.00%
Total16100.00%1100.00%


static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo) { return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins17100.00%1100.00%
Total17100.00%1100.00%

#endif /* CONFIG_NUMA && CONFIG_TMPFS */ #ifndef CONFIG_NUMA #define vm_policy vm_private_data #endif
static void shmem_pseudo_vma_init(struct vm_area_struct *vma, struct shmem_inode_info *info, pgoff_t index) { /* Create a pseudo vma that just contains the policy */ vma->vm_start = 0; /* Bias interleave by inode number to distribute better across nodes */ vma->vm_pgoff = index + info->vfs_inode.i_ino; vma->vm_ops = NULL; vma->vm_policy = mpol_shared_policy_lookup(&info->policy, index); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov59100.00%1100.00%
Total59100.00%1100.00%


static void shmem_pseudo_vma_destroy(struct vm_area_struct *vma) { /* Drop reference taken by mpol_shared_policy_lookup() */ mpol_cond_put(vma->vm_policy); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov19100.00%1100.00%
Total19100.00%1100.00%


static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp, struct shmem_inode_info *info, pgoff_t index) { struct vm_area_struct pvma; struct page *page; shmem_pseudo_vma_init(&pvma, info, index); page = swapin_readahead(swap, gfp, &pvma, 0); shmem_pseudo_vma_destroy(&pvma); return page; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov2945.31%116.67%
andrew mortonandrew morton1523.44%116.67%
hugh dickinshugh dickins1523.44%350.00%
mel gormanmel gorman57.81%116.67%
Total64100.00%6100.00%


static struct page *shmem_alloc_hugepage(gfp_t gfp, struct shmem_inode_info *info, pgoff_t index) { struct vm_area_struct pvma; struct inode *inode = &info->vfs_inode; struct address_space *mapping = inode->i_mapping; pgoff_t idx, hindex; void __rcu **results; struct page *page; if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) return NULL; hindex = round_down(index, HPAGE_PMD_NR); rcu_read_lock(); if (radix_tree_gang_lookup_slot(&mapping->page_tree, &results, &idx, hindex, 1) && idx < hindex + HPAGE_PMD_NR) { rcu_read_unlock(); return NULL; } rcu_read_unlock(); shmem_pseudo_vma_init(&pvma, info, hindex); page = alloc_pages_vma(gfp | __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN, HPAGE_PMD_ORDER, &pvma, 0, numa_node_id(), true); shmem_pseudo_vma_destroy(&pvma); if (page) prep_transhuge_page(page); return page; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov15389.47%228.57%
geert uytterhoevengeert uytterhoeven95.26%114.29%
andrew mortonandrew morton42.34%114.29%
nathan zimmernathan zimmer31.75%114.29%
hugh dickinshugh dickins21.17%228.57%
Total171100.00%7100.00%


static struct page *shmem_alloc_page(gfp_t gfp, struct shmem_inode_info *info, pgoff_t index) { struct vm_area_struct pvma; struct page *page; shmem_pseudo_vma_init(&pvma, info, index); page = alloc_page_vma(gfp, &pvma, 0); shmem_pseudo_vma_destroy(&pvma); return page; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov3355.93%125.00%
mel gormanmel gorman1525.42%125.00%
andrew mortonandrew morton915.25%125.00%
hugh dickinshugh dickins23.39%125.00%
Total59100.00%4100.00%


static struct page *shmem_alloc_and_acct_page(gfp_t gfp, struct shmem_inode_info *info, struct shmem_sb_info *sbinfo, pgoff_t index, bool huge) { struct page *page; int nr; int err = -ENOSPC; if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) huge = false; nr = huge ? HPAGE_PMD_NR : 1; if (shmem_acct_block(info->flags, nr)) goto failed; if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks - nr) > 0) goto unacct; percpu_counter_add(&sbinfo->used_blocks, nr); } if (huge) page = shmem_alloc_hugepage(gfp, info, index); else page = shmem_alloc_page(gfp, info, index); if (page) { __SetPageLocked(page); __SetPageSwapBacked(page); return page; } err = -ENOMEM; if (sbinfo->max_blocks) percpu_counter_add(&sbinfo->used_blocks, -nr); unacct: shmem_unacct_blocks(info->flags, nr); failed: return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov13667.66%218.18%
andrew mortonandrew morton3115.42%19.09%
hugh dickinshugh dickins188.96%436.36%
lee schermerhornlee schermerhorn73.48%19.09%
mel gormanmel gorman62.99%19.09%
nathan zimmernathan zimmer21.00%19.09%
al viroal viro10.50%19.09%
Total201100.00%11100.00%

/* * When a page is moved from swapcache to shmem filecache (either by the * usual swapin of shmem_getpage_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_page(struct page *page, gfp_t gfp) { return page_zonenum(page) > gfp_zone(gfp); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins25100.00%1100.00%
Total25100.00%1100.00%


static int shmem_replace_page(struct page **pagep, gfp_t gfp, struct shmem_inode_info *info, pgoff_t index) { struct page *oldpage, *newpage; struct address_space *swap_mapping; pgoff_t swap_index; int error; oldpage = *pagep; swap_index = page_private(oldpage); swap_mapping = page_mapping(oldpage); /* * 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; newpage = shmem_alloc_page(gfp, info, index); if (!newpage) return -ENOMEM; get_page(newpage); copy_highpage(newpage, oldpage); flush_dcache_page(newpage); SetPageUptodate(newpage); set_page_private(newpage, swap_index); SetPageSwapCache(newpage); /* * Our caller will very soon move newpage out of swapcache, but it's * a nice clean interface for us to replace oldpage by newpage there. */ spin_lock_irq(&swap_mapping->tree_lock); error = shmem_radix_tree_replace(swap_mapping, swap_index, oldpage, newpage); if (!error) { __inc_node_page_state(newpage, NR_FILE_PAGES); __dec_node_page_state(oldpage, NR_FILE_PAGES); } spin_unlock_irq(&swap_mapping->tree_lock); if (unlikely(error)) { /* * Is this possible? I think not, now that our callers check * both PageSwapCache and page_private after getting page lock; * but be defensive. Reverse old to newpage for clear and free. */ oldpage = newpage; } else { mem_cgroup_migrate(oldpage, newpage); lru_cache_add_anon(newpage); *pagep = newpage; } ClearPageSwapCache(oldpage); set_page_private(oldpage, 0); unlock_page(oldpage); put_page(oldpage); put_page(oldpage); return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins23097.46%350.00%
kirill a. shutemovkirill a. shutemov31.27%116.67%
mel gormanmel gorman20.85%116.67%
johannes weinerjohannes weiner10.42%116.67%
Total236100.00%6100.00%

/* * shmem_getpage_gfp - find page in cache, or get from swap, or allocate * * If we allocate a new one we do not mark it dirty. That's up to the * vm. If we swap it in we mark it dirty since we also free the swap * entry since a page cannot live in both the swap and page cache. * * fault_mm and fault_type are only supplied by shmem_fault: * otherwise they are NULL. */
static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, struct page **pagep, enum sgp_type sgp, gfp_t gfp, struct mm_struct *fault_mm, int *fault_type) { struct address_space *mapping = inode->i_mapping; struct shmem_inode_info *info; struct shmem_sb_info *sbinfo; struct mm_struct *charge_mm; struct mem_cgroup *memcg; struct page *page; swp_entry_t swap; enum sgp_type sgp_huge = sgp; pgoff_t hindex = index; int error; int once = 0; int alloced = 0; if (index > (MAX_LFS_FILESIZE >> PAGE_SHIFT)) return -EFBIG; if (sgp == SGP_NOHUGE || sgp == SGP_HUGE) sgp = SGP_CACHE; repeat: swap.val = 0; page = find_lock_entry(mapping, index); if (radix_tree_exceptional_entry(page)) { swap = radix_to_swp_entry(page); page = NULL; } if (sgp <= SGP_CACHE && ((loff_t)index << PAGE_SHIFT) >= i_size_read(inode)) { error = -EINVAL; goto unlock; } if (page && sgp == SGP_WRITE) mark_page_accessed(page); /* fallocated page? */ if (page && !PageUptodate(page)) { if (sgp != SGP_READ) goto clear; unlock_page(page); put_page(page); page = NULL; } if (page || (sgp == SGP_READ && !swap.val)) { *pagep = page; return 0; } /* * Fast cache lookup did not find it: * bring it back from swap or allocate. */ info = SHMEM_I(inode); sbinfo = SHMEM_SB(inode->i_sb); charge_mm = fault_mm ? : current->mm; if (swap.val) { /* Look it up and read it in.. */ page = lookup_swap_cache(swap); if (!page) { /* Or update major stats only when swapin succeeds?? */ if (fault_type) { *fault_type |= VM_FAULT_MAJOR; count_vm_event(PGMAJFAULT); mem_cgroup_count_vm_event(fault_mm, PGMAJFAULT); } /* Here we actually start the io */ page = shmem_swapin(swap, gfp, info, index); if (!page) { error = -ENOMEM; goto failed; } } /* We have to do this with page locked to prevent races */ lock_page(page); if (!PageSwapCache(page) || page_private(page) != swap.val || !shmem_confirm_swap(mapping, index, swap)) { error = -EEXIST; /* try again */ goto unlock; } if (!PageUptodate(page)) { error = -EIO; goto failed; } wait_on_page_writeback(page); if (shmem_should_replace_page(page, gfp)) { error = shmem_replace_page(&page, gfp, info, index); if (error) goto failed; } error = mem_cgroup_try_charge(page, charge_mm, gfp, &memcg, false); if (!error) { error = shmem_add_to_page_cache(page, mapping, index, swp_to_radix_entry(swap)); /* * We already confirmed swap under page lock, and make * no memory allocation here, so usually no possibility * of error; but free_swap_and_cache() only trylocks a * page, so it is just possible that the entry has been * truncated or holepunched since swap was confirmed. * shmem_undo_range() will have done some of the * unaccounting, now delete_from_swap_cache() will do * the rest. * Reset swap.val? No, leave it so "failed" goes back to * "repeat": reading a hole and writing should succeed. */ if (error) { mem_cgroup_cancel_charge(page, memcg, false); delete_from_swap_cache(page); } } if (error) goto failed; mem_cgroup_commit_charge(page, memcg, true, false); spin_lock_irq(&info->lock); info->swapped--; shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); if (sgp == SGP_WRITE) mark_page_accessed(page); delete_from_swap_cache(page); set_page_dirty(page); swap_free(swap); } else { /* shmem_symlink() */ if (mapping->a_ops != &shmem_aops) goto alloc_nohuge; if (shmem_huge == SHMEM_HUGE_DENY || sgp_huge == SGP_NOHUGE) goto alloc_nohuge; if (shmem_huge == SHMEM_HUGE_FORCE) goto alloc_huge; switch (sbinfo->huge) { loff_t i_size; pgoff_t off; case SHMEM_HUGE_NEVER: goto alloc_nohuge; case SHMEM_HUGE_WITHIN_SIZE: off = round_up(index, HPAGE_PMD_NR); i_size = round_up(i_size_read(inode), PAGE_SIZE); if (i_size >= HPAGE_PMD_SIZE && i_size >> PAGE_SHIFT >= off) goto alloc_huge; /* fallthrough */ case SHMEM_HUGE_ADVISE: if (sgp_huge == SGP_HUGE) goto alloc_huge; /* TODO: implement fadvise() hints */ goto alloc_nohuge; } alloc_huge: page = shmem_alloc_and_acct_page(gfp, info, sbinfo, index, true); if (IS_ERR(page)) { alloc_nohuge: page = shmem_alloc_and_acct_page(gfp, info, sbinfo, index, false); } if (IS_ERR(page)) { int retry = 5; error = PTR_ERR(page); page = NULL; if (error != -ENOSPC) goto failed; /* * Try to reclaim some spece by splitting a huge page * beyond i_size on the filesystem. */ while (retry--) { int ret; ret = shmem_unused_huge_shrink(sbinfo, NULL, 1); if (ret == SHRINK_STOP) break; if (ret) goto alloc_nohuge; } goto failed; } if (PageTransHuge(page)) hindex = round_down(index, HPAGE_PMD_NR); else hindex = index; if (sgp == SGP_WRITE) __SetPageReferenced(page); error = mem_cgroup_try_charge(page, charge_mm, gfp, &memcg, PageTransHuge(page)); if (error) goto unacct; error = radix_tree_maybe_preload_order(gfp & GFP_RECLAIM_MASK, compound_order(page)); if (!error) { error = shmem_add_to_page_cache(page, mapping, hindex, NULL); radix_tree_preload_end(); } if (error) { mem_cgroup_cancel_charge(page, memcg, PageTransHuge(page)); goto unacct; } mem_cgroup_commit_charge(page, memcg, false, PageTransHuge(page)); lru_cache_add_anon(page); spin_lock_irq(&info->lock); info->alloced += 1 << compound_order(page); inode->i_blocks += BLOCKS_PER_PAGE << compound_order(page); shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); alloced = true; if (PageTransHuge(page) && DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) < hindex + HPAGE_PMD_NR - 1) { /* * Part of the huge page is beyond i_size: subject * to shrink under memory pressure. */ spin_lock(&sbinfo->shrinklist_lock); if (list_empty(&info->shrinklist)) { list_add_tail(&info->shrinklist, &sbinfo->shrinklist); sbinfo->shrinklist_len++; } spin_unlock(&sbinfo->shrinklist_lock); } /* * Let SGP_FALLOC use the SGP_WRITE optimization on a new page. */ if (sgp == SGP_FALLOC) sgp = SGP_WRITE; clear: /* * Let SGP_WRITE caller clear ends if write does not fill page; * but SGP_FALLOC on a page fallocated earlier must initialize * it now, lest undo on failure cancel our earlier guarantee. */ if (sgp != SGP_WRITE && !PageUptodate(page)) { struct page *head = compound_head(page); int i; for (i = 0; i < (1 << compound_order(head)); i++) { clear_highpage(head + i); flush_dcache_page(head + i); } SetPageUptodate(head); } } /* Perhaps the file has been truncated since we checked */ if (sgp <= SGP_CACHE && ((loff_t)index << PAGE_SHIFT) >= i_size_read(inode)) { if (alloced) { ClearPageDirty(page); delete_from_page_cache(page); spin_lock_irq(&info->lock); shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); } error = -EINVAL; goto unlock; } *pagep = page + index - hindex; return 0; /* * Error recovery. */ unacct: if (sbinfo->max_blocks) percpu_counter_sub(&sbinfo->used_blocks, 1 << compound_order(page)); shmem_unacct_blocks(info->flags, 1 << compound_order(page)); if (PageTransHuge(page)) { unlock_page(page); put_page(page); goto alloc_nohuge; } failed: if (swap.val && !shmem_confirm_swap(mapping, index, swap)) error = -EEXIST; unlock: if (page) { unlock_page(page); put_page(page); } if (error == -ENOSPC && !once++) { info = SHMEM_I(inode); spin_lock_irq(&info->lock); shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); goto repeat; } if (error == -EEXIST) /* from above or from radix_tree_insert */ goto repeat; return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins68849.04%3652.94%
kirill a. shutemovkirill a. shutemov44231.50%68.82%
linus torvaldslinus torvalds866.13%34.41%
andrew mortonandrew morton654.63%1319.12%
johannes weinerjohannes weiner422.99%22.94%
andres lagar-cavillaandres lagar-cavilla372.64%11.47%
li shaohuali shaohua161.14%11.47%
pre-gitpre-git151.07%11.47%
tim chentim chen70.50%11.47%
nick pigginnick piggin20.14%22.94%
kamezawa hiroyukikamezawa hiroyuki20.14%11.47%
vladimir davydovvladimir davydov10.07%11.47%
Total1403100.00%68100.00%


static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct inode *inode = file_inode(vma->vm_file); gfp_t gfp = mapping_gfp_mask(inode->i_mapping); enum sgp_type sgp; int error; int ret = VM_FAULT_LOCKED; /* * Trinity finds that probing a hole which tmpfs is punching can * prevent the hole-punch from ever completing: which in turn * locks writers out with its hold on i_mutex. So refrain from * faulting pages into the hole while it's being punched. Although * shmem_undo_range() does remove the additions, it may be unable to * keep up, as each new page needs its own unmap_mapping_range() call, * and the i_mmap tree grows ever slower to scan if new vmas are added. * * It does not matter if we sometimes reach this check just before the * hole-punch begins, so that one fault then races with the punch: * we just need to make racing faults a rare case. * * The implementation below would be much simpler if we just used a * standard mutex or completion: but we cannot take i_mutex in fault, * and bloating every shmem inode for this unlikely case would be sad. */ if (unlikely(inode->i_private)) { struct shmem_falloc *shmem_falloc; spin_lock(&inode->i_lock); shmem_falloc = inode->i_private; if (shmem_falloc && shmem_falloc->waitq && vmf->pgoff >= shmem_falloc->start && vmf->pgoff < shmem_falloc->next) { wait_queue_head_t *shmem_falloc_waitq; DEFINE_WAIT(shmem_fault_wait); ret = VM_FAULT_NOPAGE; if ((vmf->flags & FAULT_FLAG_ALLOW_RETRY) && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { /* It's polite to up mmap_sem if we can */ up_read(&vma->vm_mm->mmap_sem); ret = VM_FAULT_RETRY; } shmem_falloc_waitq = shmem_falloc->waitq; prepare_to_wait(shmem_falloc_waitq, &shmem_fault_wait, TASK_UNINTERRUPTIBLE); spin_unlock(&inode->i_lock); schedule(); /* * shmem_falloc_waitq points into the shmem_fallocate() * stack of the hole-punching task: shmem_falloc_waitq * is usually invalid by the time we reach here, but * finish_wait() does not dereference it in that case; * though i_lock needed lest racing with wake_up_all(). */ spin_lock(&inode->i_lock); finish_wait(shmem_falloc_waitq, &shmem_fault_wait); spin_unlock(&inode->i_lock); return ret; } spin_unlock(&inode->i_lock); } sgp = SGP_CACHE; if (vma->vm_flags & VM_HUGEPAGE) sgp = SGP_HUGE; else if (vma->vm_flags & VM_NOHUGEPAGE) sgp = SGP_NOHUGE; error = shmem_getpage_gfp(inode, vmf->pgoff, &vmf->page, sgp, gfp, vma->vm_mm, &ret); if (error) return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins18863.51%425.00%
kirill a. shutemovkirill a. shutemov3411.49%16.25%
linus torvaldslinus torvalds248.11%212.50%
nick pigginnick piggin206.76%212.50%
andres lagar-cavillaandres lagar-cavilla175.74%16.25%
andrew mortonandrew morton82.70%318.75%
al viroal viro31.01%16.25%
pre-gitpre-git10.34%16.25%
adrian bunkadrian bunk10.34%16.25%
Total296100.00%16100.00%


unsigned long shmem_get_unmapped_area(struct file *file, unsigned long uaddr, unsigned long len, unsigned long pgoff, unsigned long flags) { unsigned long (*get_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); unsigned long addr; unsigned long offset; unsigned long inflated_len; unsigned long inflated_addr; unsigned long inflated_offset; if (len > TASK_SIZE) return -ENOMEM; get_area = current->mm->get_unmapped_area; addr = get_area(file, uaddr, len, pgoff, flags); if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) return addr; if (IS_ERR_VALUE(addr)) return addr; if (addr & ~PAGE_MASK) return addr; if (addr > TASK_SIZE - len) return addr; if (shmem_huge == SHMEM_HUGE_DENY) return addr; if (len < HPAGE_PMD_SIZE) return addr; if (flags & MAP_FIXED) return addr; /* * Our priority is to support MAP_SHARED mapped hugely; * and support MAP_PRIVATE mapped hugely too, until it is COWed. * But if caller specified an address hint, respect that as before. */ if (uaddr) return addr; if (shmem_huge != SHMEM_HUGE_FORCE) { struct super_block *sb; if (file) { VM_BUG_ON(file->f_op != &shmem_file_operations); sb = file_inode(file)->i_sb; } else { /* * Called directly from mm/mmap.c, or drivers/char/mem.c * for "/dev/zero", to create a shared anonymous object. */ if (IS_ERR(shm_mnt)) return addr; sb = shm_mnt->mnt_sb; } if (SHMEM_SB(sb)->huge == SHMEM_HUGE_NEVER) return addr; } offset = (pgoff << PAGE_SHIFT) & (HPAGE_PMD_SIZE-1); if (offset && offset + len < 2 * HPAGE_PMD_SIZE) return addr; if ((addr & (HPAGE_PMD_SIZE-1)) == offset) return addr; inflated_len = len + HPAGE_PMD_SIZE - PAGE_SIZE; if (inflated_len > TASK_SIZE) return addr; if (inflated_len < len) return addr; inflated_addr = get_area(NULL, 0, inflated_len, 0, flags); if (IS_ERR_VALUE(inflated_addr)) return addr; if (inflated_addr & ~PAGE_MASK) return addr; inflated_offset = inflated_addr & (HPAGE_PMD_SIZE-1); inflated_addr += offset - inflated_offset; if (inflated_offset > offset) inflated_addr += HPAGE_PMD_SIZE; if (inflated_addr > TASK_SIZE - len) return addr; return inflated_addr; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins39899.50%133.33%
toshi kanitoshi kani10.25%133.33%
kirill a. shutemovkirill a. shutemov10.25%133.33%
Total400100.00%3100.00%

#ifdef CONFIG_NUMA
static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpol) { struct inode *inode = file_inode(vma->vm_file); return mpol_set_shared_policy(&SHMEM_I(inode)->policy, vma, mpol); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton3681.82%125.00%
hugh dickinshugh dickins49.09%125.00%
al viroal viro36.82%125.00%
adrian bunkadrian bunk12.27%125.00%
Total44100.00%4100.00%


static struct mempolicy *shmem_get_policy(struct vm_area_struct *vma, unsigned long addr) { struct inode *inode = file_inode(vma->vm_file); pgoff_t index; index = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; return mpol_shared_policy_lookup(&SHMEM_I(inode)->policy, index); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton5484.38%125.00%
hugh dickinshugh dickins69.38%125.00%
al viroal viro34.69%125.00%
adrian bunkadrian bunk11.56%125.00%
Total64100.00%4100.00%

#endif
int shmem_lock(struct file *file, int lock, struct user_struct *user) { struct inode *inode = file_inode(file); struct shmem_inode_info *info = SHMEM_I(inode); int retval = -ENOMEM; spin_lock_irq(&info->lock); if (lock && !(info->flags & VM_LOCKED)) { if (!user_shm_lock(inode->i_size, user)) goto out_nomem; info->flags |= VM_LOCKED; mapping_set_unevictable(file->f_mapping); } if (!lock && (info->flags & VM_LOCKED) && user) { user_shm_unlock(inode->i_size, user); info->flags &= ~VM_LOCKED; mapping_clear_unevictable(file->f_mapping); } retval = 0; out_nomem: spin_unlock_irq(&info->lock); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
rik van rielrik van riel7348.03%114.29%
linus torvaldslinus torvalds4428.95%228.57%
hugh dickinshugh dickins1610.53%114.29%
lee schermerhornlee schermerhorn149.21%114.29%
al viroal viro31.97%114.29%
kirill a. shutemovkirill a. shutemov21.32%114.29%
Total152100.00%7100.00%


static int shmem_mmap(struct file *file, struct vm_area_struct *vma) { file_accessed(file); vma->vm_ops = &shmem_vm_ops; if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && ((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) < (vma->vm_end & HPAGE_PMD_MASK)) { khugepaged_enter(vma, vma->vm_flags); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov3955.71%240.00%
linus torvaldslinus torvalds2637.14%120.00%
al viroal viro45.71%120.00%
adrian bunkadrian bunk11.43%120.00%
Total70100.00%5100.00%


static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev, unsigned long flags) { struct inode *inode; struct shmem_inode_info *info; struct shmem_sb_info *sbinfo = SHMEM_SB(sb); if (shmem_reserve_inode(sb)) return NULL; inode = new_inode(sb); if (inode) { inode->i_ino = get_next_ino(); inode_init_owner(inode, dir, mode); inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_generation = get_seconds(); info = SHMEM_I(inode); memset(info, 0, (char *)inode - (char *)info); spin_lock_init(&info->lock); info->seals = F_SEAL_SEAL; info->flags = flags & VM_NORESERVE; INIT_LIST_HEAD(&info->shrinklist); INIT_LIST_HEAD(&info->swaplist); simple_xattrs_init(&info->xattrs); cache_no_acl(inode); switch (mode & S_IFMT) { default: inode->i_op = &shmem_special_inode_operations; init_special_inode(inode, mode, dev); break; case S_IFREG: inode->i_mapping->a_ops = &shmem_aops; inode->i_op = &shmem_inode_operations; inode->i_fop = &shmem_file_operations; mpol_shared_policy_init(&info->policy, shmem_get_sbmpol(sbinfo)); break; case S_IFDIR: inc_nlink(inode); /* Some things misbehave if size == 0 on a directory */ inode->i_size = 2 * BOGO_DIRENT_SIZE; inode->i_op = &shmem_dir_inode_operations; inode->i_fop = &simple_dir_operations; break; case S_IFLNK: /* * Must not load anything in the rbtree, * mpol_free_shared_policy will not be called. */ mpol_shared_policy_init(&info->policy, NULL); break; } } else shmem_free_inode(sb); return inode; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds15850.32%310.71%
hugh dickinshugh dickins5818.47%725.00%
andrea arcangeliandrea arcangeli175.41%13.57%
dmitriy monakhovdmitriy monakhov123.82%13.57%
kirill a. shutemovkirill a. shutemov82.55%13.57%
andreas gruenbacherandreas gruenbacher72.23%13.57%
christoph hellwigchristoph hellwig72.23%13.57%
david m. grimesdavid m. grimes72.23%13.57%
al viroal viro72.23%310.71%
eric pariseric paris61.91%13.57%
david herrmanndavid herrmann61.91%13.57%
pavel emelianovpavel emelianov51.59%13.57%
robin holtrobin holt41.27%13.57%
dave hansendave hansen30.96%13.57%
lee schermerhornlee schermerhorn30.96%13.57%
pre-gitpre-git20.64%13.57%
andries brouwerandries brouwer20.64%13.57%
aristeu sergio rozanski filhoaristeu sergio rozanski filho20.64%13.57%
Total314100.00%28100.00%


bool shmem_mapping(struct address_space *mapping) { if (!mapping->host) return false; return mapping->host->i_sb->s_op == &shmem_ops; }

Contributors

PersonTokensPropCommitsCommitProp
johannes weinerjohannes weiner1650.00%133.33%
sasha levinsasha levin1031.25%133.33%
christoph hellwigchristoph hellwig618.75%133.33%
Total32100.00%3100.00%

#ifdef CONFIG_TMPFS static const struct inode_operations shmem_symlink_inode_operations; static const struct inode_operations shmem_short_symlink_operations; #ifdef CONFIG_TMPFS_XATTR static int shmem_initxattrs(struct inode *, const struct xattr *, void *); #else #define shmem_initxattrs NULL #endif
static int shmem_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { struct inode *inode = mapping->host; struct shmem_inode_info *info = SHMEM_I(inode); pgoff_t index = pos >> PAGE_SHIFT; /* i_mutex is held by caller */ if (unlikely(info->seals)) { if (info->seals & F_SEAL_WRITE) return -EPERM; if ((info->seals & F_SEAL_GROW) && pos + len > inode->i_size) return -EPERM; } return shmem_getpage(inode, index, pagep, SGP_WRITE); }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann5646.67%120.00%
andrew mortonandrew morton3630.00%120.00%
nick pigginnick piggin2621.67%120.00%
kirill a. shutemovkirill a. shutemov10.83%120.00%
hugh dickinshugh dickins10.83%120.00%
Total120100.00%5100.00%


static int shmem_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { struct inode *inode = mapping->host; if (pos + copied > inode->i_size) i_size_write(inode, pos + copied); if (!PageUptodate(page)) { struct page *head = compound_head(page); if (PageTransCompound(page)) { int i; for (i = 0; i < HPAGE_PMD_NR; i++) { if (head + i == page) continue; clear_highpage(head + i); flush_dcache_page(head + i); } } if (copied < PAGE_SIZE) { unsigned from = pos & (PAGE_SIZE - 1); zero_user_segments(page, 0, from, from + copied, PAGE_SIZE); } SetPageUptodate(head); } set_page_dirty(page); unlock_page(page); put_page(page); return copied; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov6534.39%228.57%
nick pigginnick piggin6433.86%114.29%
hugh dickinshugh dickins5730.16%228.57%
fengguang wufengguang wu21.06%114.29%
linus torvaldslinus torvalds10.53%114.29%
Total189100.00%7100.00%


static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct address_space *mapping = inode->i_mapping; pgoff_t index; unsigned long offset; enum sgp_type sgp = SGP_READ; int error = 0; ssize_t retval = 0; loff_t *ppos = &iocb->ki_pos; /* * Might this read be for a stacking filesystem? Then when reading * holes of a sparse file, we actually need to allocate those pages, * and even mark them dirty, so it cannot exceed the max_blocks limit. */ if (!iter_is_iovec(to)) sgp = SGP_CACHE; index = *ppos >> PAGE_SHIFT; offset = *ppos & ~PAGE_MASK; for (;;) { struct page *page = NULL; pgoff_t end_index; unsigned long nr, ret; loff_t i_size = i_size_read(inode); end_index = i_size >> PAGE_SHIFT; if (index > end_index) break; if (index == end_index) { nr = i_size & ~PAGE_MASK; if (nr <= offset) break; } error = shmem_getpage(inode, index, &page, sgp); if (error) { if (error == -EINVAL) error = 0; break; } if (page) { if (sgp == SGP_CACHE) set_page_dirty(page); unlock_page(page); } /* * We must evaluate after, since reads (unlike writes) * are called without i_mutex protection against truncate */ nr = PAGE_SIZE; i_size = i_size_read(inode); end_index = i_size >> PAGE_SHIFT; if (index == end_index) { nr = i_size & ~PAGE_MASK; if (nr <= offset) { if (page) put_page(page); break; } } nr -= offset; if (page) { /* * If users can be writing to this page using arbitrary * virtual addresses, take care about potential aliasing * before reading the page on the kernel side. */ if (mapping_writably_mapped(mapping)) flush_dcache_page(page); /* * Mark the page accessed if we read the beginning. */ if (!offset) mark_page_accessed(page); } else { page = ZERO_PAGE(0); get_page(page); } /* * Ok, we have the page, and it's up-to-date, so * now we can copy it to user space... */ ret = copy_page_to_iter(page, offset, nr, to); retval += ret; offset += ret; index += offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; put_page(page); if (!iov_iter_count(to)) break; if (ret < nr) { error = -EFAULT; break; } cond_resched(); } *ppos = ((loff_t) index << PAGE_SHIFT) + offset; file_accessed(file); return retval ? retval : error; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds14535.02%13.70%
hugh dickinshugh dickins11828.50%725.93%
al viroal viro6716.18%622.22%
andrew mortonandrew morton6014.49%829.63%
kirill a. shutemovkirill a. shutemov133.14%13.70%
nick pigginnick piggin61.45%13.70%
ingo molnaringo molnar20.48%13.70%
geert uytterhoevengeert uytterhoeven20.48%13.70%
jes sorensenjes sorensen10.24%13.70%
Total414100.00%27100.00%


static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { struct address_space *mapping = in->f_mapping; struct inode *inode = mapping->host; unsigned int loff, nr_pages, req_pages; struct page *pages[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct page *page; pgoff_t index, end_index; loff_t isize, left; int error, page_nr; struct splice_pipe_desc spd = { .pages = pages, .partial = partial, .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &page_cache_pipe_buf_ops, .spd_release = spd_release_page, }; isize = i_size_read(inode); if (unlikely(*ppos >= isize)) return 0; left = isize - *ppos; if (unlikely(left < len)) len = left; if (splice_grow_spd(pipe, &spd)) return -ENOMEM; index = *ppos >> PAGE_SHIFT; loff = *ppos & ~PAGE_MASK; req_pages = (len + loff + PAGE_SIZE - 1) >> PAGE_SHIFT; nr_pages = min(req_pages, spd.nr_pages_max); spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, spd.pages); index += spd.nr_pages; error = 0; while (spd.nr_pages < nr_pages) { error = shmem_getpage(inode, index, &page, SGP_CACHE); if (error) break; unlock_page(page); spd.pages[spd.nr_pages++] = page; index++; } index = *ppos >> PAGE_SHIFT; nr_pages = spd.nr_pages; spd.nr_pages = 0; for (page_nr = 0; page_nr < nr_pages; page_nr++) { unsigned int this_len; if (!len) break; this_len = min_t(unsigned long, len, PAGE_SIZE - loff); page = spd.pages[page_nr]; if (!PageUptodate(page) || page->mapping != mapping) { error = shmem_getpage(inode, index, &page, SGP_CACHE); if (error) break; unlock_page(page); put_page(spd.pages[page_nr]); spd.pages[page_nr] = page; } isize = i_size_read(inode); end_index = (isize - 1) >> PAGE_SHIFT; if (unlikely(!isize || index > end_index)) break; if (end_index == index) { unsigned int plen; plen = ((isize - 1) & ~PAGE_MASK) + 1; if (plen <= loff) break; this_len = min(this_len, plen - loff); len = this_len; } spd.partial[page_nr].offset = loff; spd.partial[page_nr].len = this_len; len -= this_len; loff = 0; spd.nr_pages++; index++; } while (page_nr < nr_pages) put_page(spd.pages[page_nr++]); if (spd.nr_pages) error = splice_to_pipe(pipe, &spd); splice_shrink_spd(&spd); if (error > 0) { *ppos += error; file_accessed(in); } return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins55593.28%337.50%
linus torvaldslinus torvalds172.86%112.50%
kirill a. shutemovkirill a. shutemov101.68%112.50%
eric dumazeteric dumazet50.84%112.50%
andrew mortonandrew morton50.84%112.50%
al viroal viro30.50%112.50%
Total595100.00%8100.00%

/* * llseek SEEK_DATA or SEEK_HOLE through the radix_tree. */
static pgoff_t shmem_seek_hole_data(struct address_space *mapping, pgoff_t index, pgoff_t end, int whence) { struct page *page; struct pagevec pvec; pgoff_t indices[PAGEVEC_SIZE]; bool done = false; int i; pagevec_init(&pvec, 0); pvec.nr = 1; /* start small: we may be there already */ while (!done) { pvec.nr = find_get_entries(mapping, index, pvec.nr, pvec.pages, indices); if (!pvec.nr) { if (whence == SEEK_DATA) index = end; break; } for (i = 0; i < pvec.nr; i++, index++) { if (index < indices[i]) { if (whence == SEEK_HOLE) { done = true; break; } index = indices[i]; } page = pvec.pages[i]; if (page && !radix_tree_exceptional_entry(page)) { if (!PageUptodate(page)) page = NULL; } if (index >= end || (page && whence == SEEK_DATA) || (!page && whence == SEEK_HOLE)) { done = true; break; } } pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); pvec.nr = PAGEVEC_SIZE; cond_resched(); } return index; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins23797.13%133.33%
andrew mortonandrew morton52.05%133.33%
johannes weinerjohannes weiner20.82%133.33%
Total244100.00%3100.00%


static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence) { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; pgoff_t start, end; loff_t new_offset; if (whence != SEEK_DATA && whence != SEEK_HOLE) return generic_file_llseek_size(file, offset, whence, MAX_LFS_FILESIZE, i_size_read(inode)); inode_lock(inode); /* We're holding i_mutex so we can access i_size directly */ if (offset < 0) offset = -EINVAL; else if (offset >= inode->i_size) offset = -ENXIO; else { start = offset >> PAGE_SHIFT; end = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; new_offset = shmem_seek_hole_data(mapping, start, end, whence); new_offset <<= PAGE_SHIFT; if (new_offset > offset) { if (new_offset < inode->i_size) offset = new_offset; else if (whence == SEEK_DATA) offset = -ENXIO; else offset = inode->i_size; } } if (offset >= 0) offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE); inode_unlock(inode); return offset; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins18690.73%233.33%
jeff liujeff liu73.41%116.67%
andrew mortonandrew morton62.93%116.67%
kirill a. shutemovkirill a. shutemov41.95%116.67%
al viroal viro20.98%116.67%
Total205100.00%6100.00%

/* * We need a tag: a new tag would expand every radix_tree_node by 8 bytes, * so reuse a tag which we firmly believe is never set or cleared on shmem. */ #define SHMEM_TAG_PINNED PAGECACHE_TAG_TOWRITE #define LAST_SCAN 4 /* about 150ms max */
static void shmem_tag_pins(struct address_space *mapping) { struct radix_tree_iter iter; void **slot; pgoff_t start; struct page *page; lru_add_drain(); start = 0; rcu_read_lock(); radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) { page = radix_tree_deref_slot(slot); if (!page || radix_tree_exception(page)) { if (radix_tree_deref_retry(page)) { slot = radix_tree_iter_retry(&iter); continue; } } else if (page_count(page) - page_mapcount(page) > 1) { spin_lock_irq(&mapping->tree_lock); radix_tree_tag_set(&mapping->page_tree, iter.index, SHMEM_TAG_PINNED); spin_unlock_irq(&mapping->tree_lock); } if (need_resched()) { cond_resched_rcu(); slot = radix_tree_iter_next(&iter); } } rcu_read_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann13989.68%133.33%
matthew wilcoxmatthew wilcox1610.32%266.67%
Total155100.00%3100.00%

/* * Setting SEAL_WRITE requires us to verify there's no pending writer. However, * via get_user_pages(), drivers might have some pending I/O without any active * user-space mappings (eg., direct-IO, AIO). Therefore, we look at all pages * and see whether it has an elevated ref-count. If so, we tag them and wait for * them to be dropped. * The caller must guarantee that no new user will acquire writable references * to those pages to avoid races. */
static int shmem_wait_for_pins(struct address_space *mapping) { struct radix_tree_iter iter; void **slot; pgoff_t start; struct page *page; int error, scan; shmem_tag_pins(mapping); error = 0; for (scan = 0; scan <= LAST_SCAN; scan++) { if (!radix_tree_tagged(&mapping->page_tree, SHMEM_TAG_PINNED)) break; if (!scan) lru_add_drain_all(); else if (schedule_timeout_killable((HZ << scan) / 200)) scan = LAST_SCAN; start = 0; rcu_read_lock(); radix_tree_for_each_tagged(slot, &mapping->page_tree, &iter, start, SHMEM_TAG_PINNED) { page = radix_tree_deref_slot(slot); if (radix_tree_exception(page)) { if (radix_tree_deref_retry(page)) { slot = radix_tree_iter_retry(&iter); continue; } page = NULL; } if (page && page_count(page) - page_mapcount(page) != 1) { if (scan < LAST_SCAN) goto continue_resched; /* * On the last scan, we clean up all those tags * we inserted; but make a note that we still * found pages pinned. */ error = -EBUSY; } spin_lock_irq(&mapping->tree_lock); radix_tree_tag_clear(&mapping->page_tree, iter.index, SHMEM_TAG_PINNED); spin_unlock_irq(&mapping->tree_lock); continue_resched: if (need_resched()) { cond_resched_rcu(); slot = radix_tree_iter_next(&iter); } } rcu_read_unlock(); } return error; }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann22993.47%250.00%
matthew wilcoxmatthew wilcox166.53%250.00%
Total245100.00%4100.00%

#define F_ALL_SEALS (F_SEAL_SEAL | \ F_SEAL_SHRINK | \ F_SEAL_GROW | \ F_SEAL_WRITE)
int shmem_add_seals(struct file *file, unsigned int seals) { struct inode *inode = file_inode(file); struct shmem_inode_info *info = SHMEM_I(inode); int error; /* * SEALING * Sealing allows multiple parties to share a shmem-file but restrict * access to a specific subset of file operations. Seals can only be * added, but never removed. This way, mutually untrusted parties can * share common memory regions with a well-defined policy. A malicious * peer can thus never perform unwanted operations on a shared object. * * Seals are only supported on special shmem-files and always affect * the whole underlying inode. Once a seal is set, it may prevent some * kinds of access to the file. Currently, the following seals are * defined: * SEAL_SEAL: Prevent further seals from being set on this file * SEAL_SHRINK: Prevent the file from shrinking * SEAL_GROW: Prevent the file from growing * SEAL_WRITE: Prevent write access to the file * * As we don't require any trust relationship between two parties, we * must prevent seals from being removed. Therefore, sealing a file * only adds a given set of seals to the file, it never touches * existing seals. Furthermore, the "setting seals"-operation can be * sealed itself, which basically prevents any further seal from being * added. * * Semantics of sealing are only defined on volatile files. Only * anonymous shmem files support sealing. More importantly, seals are * never written to disk. Therefore, there's no plan to support it on * other file types. */ if (file->f_op != &shmem_file_operations) return -EINVAL; if (!(file->f_mode & FMODE_WRITE)) return -EPERM; if (seals & ~(unsigned int)F_ALL_SEALS) return -EINVAL; inode_lock(inode); if (info->seals & F_SEAL_SEAL) { error = -EPERM; goto unlock; } if ((seals & F_SEAL_WRITE) && !(info->seals & F_SEAL_WRITE)) { error = mapping_deny_writable(file->f_mapping); if (error) goto unlock; error = shmem_wait_for_pins(file->f_mapping); if (error) { mapping_allow_writable(file->f_mapping); goto unlock; } } info->seals |= seals; error = 0; unlock: inode_unlock(inode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann18298.91%150.00%
al viroal viro21.09%150.00%
Total184100.00%2100.00%

EXPORT_SYMBOL_GPL(shmem_add_seals);
int shmem_get_seals(struct file *file) { if (file->f_op != &shmem_file_operations) return -EINVAL; return SHMEM_I(file_inode(file))->seals; }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann34100.00%1100.00%
Total34100.00%1100.00%

EXPORT_SYMBOL_GPL(shmem_get_seals);
long shmem_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { long error; switch (cmd) { case F_ADD_SEALS: /* disallow upper 32bit */ if (arg > UINT_MAX) return -EINVAL; error = shmem_add_seals(file, arg); break; case F_GET_SEALS: error = shmem_get_seals(file); break; default: error = -EINVAL; break; } return error; }

Contributors

PersonTokensPropCommitsCommitProp
david herrmanndavid herrmann72100.00%1100.00%
Total72100.00%1100.00%


static long shmem_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file_inode(file); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_falloc shmem_falloc; pgoff_t start, index, end; int error; if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) return -EOPNOTSUPP; inode_lock(inode); if (mode & FALLOC_FL_PUNCH_HOLE) { struct address_space *mapping = file->f_mapping; loff_t unmap_start = round_up(offset, PAGE_SIZE); loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq); /* protected by i_mutex */ if (info->seals & F_SEAL_WRITE) { error = -EPERM; goto out; } shmem_falloc.waitq = &shmem_falloc_waitq; shmem_falloc.start = unmap_start >> PAGE_SHIFT; shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; spin_lock(&inode->i_lock); inode->i_private = &shmem_falloc; spin_unlock(&inode->i_lock); if ((u64)unmap_end > (u64)unmap_start) unmap_mapping_range(mapping, unmap_start, 1 + unmap_end - unmap_start, 0); shmem_truncate_range(inode, offset, offset + len - 1); /* No need to unmap again: hole-punching leaves COWed pages */ spin_lock(&inode->i_lock); inode->i_private = NULL; wake_up_all(&shmem_falloc_waitq); spin_unlock(&inode->i_lock); error = 0; goto out; } /* We need to check rlimit even when FALLOC_FL_KEEP_SIZE */ error = inode_newsize_ok(inode, offset + len); if (error) goto out; if ((info->seals & F_SEAL_GROW) && offset + len > inode->i_size) { error = -EPERM; goto out; } start = offset >> PAGE_SHIFT; end = (offset + len + PAGE_SIZE - 1) >> PAGE_SHIFT; /* Try to avoid a swapstorm if len is impossible to satisfy */ if (sbinfo->max_blocks && end - start > sbinfo->max_blocks) { error = -ENOSPC; goto out; } shmem_falloc.waitq = NULL; shmem_falloc.start = start; shmem_falloc.next = start; shmem_falloc.nr_falloced = 0; shmem_falloc.nr_unswapped = 0; spin_lock(&inode->i_lock); inode->i_private = &shmem_falloc; spin_unlock(&inode->i_lock); for (index = start; index < end; index++) { struct page *page; /* * Good, the fallocate(2) manpage permits EINTR: we may have * been interrupted because we are using up too much memory. */ if (signal_pending(current)) error = -EINTR; else if (shmem_falloc.nr_unswapped > shmem_falloc.nr_falloced) error = -ENOMEM; else error = shmem_getpage(inode, index, &page, SGP_FALLOC); if (error) { /* Remove the !PageUptodate pages we added */ if (index > start) { shmem_undo_range(inode, (loff_t)start << PAGE_SHIFT, ((loff_t)index << PAGE_SHIFT) - 1, true); } goto undone; } /* * Inform shmem_writepage() how far we have reached. * No need for lock or barrier: we have the page lock. */ shmem_falloc.next++; if (!PageUptodate(page)) shmem_falloc.nr_falloced++; /* * If !PageUptodate, leave it that way so that freeable pages * can be recognized if we need to rollback on error later. * But set_page_dirty so that memory pressure will swap rather * than free the pages we are allocating (and SGP_CACHE pages * might still be clean: we now need to mark those dirty too). */ set_page_dirty(page); unlock_page(page); put_page(page); cond_resched(); } if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + len > inode->i_size) i_size_write(inode, offset + len); inode->i_ctime = CURRENT_TIME; undone: spin_lock(&inode->i_lock); inode->i_private = NULL; spin_unlock(&inode->i_lock); out: inode_unlock(inode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins56188.63%861.54%
david herrmanndavid herrmann579.00%17.69%
kirill a. shutemovkirill a. shutemov60.95%17.69%
al viroal viro50.79%215.38%
anthony romanoanthony romano40.63%17.69%
Total633100.00%13100.00%


static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) { struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb); buf->f_type = TMPFS_MAGIC; buf->f_bsize = PAGE_SIZE; buf->f_namelen = NAME_MAX; if (sbinfo->max_blocks) { buf->f_blocks = sbinfo->max_blocks; buf->f_bavail = buf->f_bfree = sbinfo->max_blocks - percpu_counter_sum(&sbinfo->used_blocks); } if (sbinfo->max_inodes) { buf->f_files = sbinfo->max_inodes; buf->f_ffree = sbinfo->free_inodes; } /* else leave those fields 0 like simple_statfs */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds7164.55%225.00%
hugh dickinshugh dickins2320.91%225.00%
tim chentim chen98.18%112.50%
david howellsdavid howells54.55%112.50%
kirill a. shutemovkirill a. shutemov10.91%112.50%
andrew mortonandrew morton10.91%112.50%
Total110100.00%8100.00%

/* * File creation. Allocate an inode, and we're done.. */
static int shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { struct inode *inode; int error = -ENOSPC; inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); if (inode) { error = simple_acl_create(dir, inode); if (error) goto out_iput; error = security_inode_init_security(inode, dir, &dentry->d_name, shmem_initxattrs, NULL); if (error && error != -EOPNOTSUPP) goto out_iput; error = 0; dir->i_size += BOGO_DIRENT_SIZE; dir->i_ctime = dir->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ } return error; out_iput: iput(inode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds6241.89%18.33%
hugh dickinshugh dickins2214.86%325.00%
christoph hellwigchristoph hellwig2013.51%18.33%
mimi zoharmimi zohar1510.14%18.33%
stephen d. smalleystephen d. smalley128.11%18.33%
andreas gruenbacherandreas gruenbacher96.08%18.33%
al viroal viro53.38%216.67%
dmitriy monakhovdmitriy monakhov21.35%18.33%
andries brouwerandries brouwer10.68%18.33%
Total148100.00%12100.00%


static int shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; int error = -ENOSPC; inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE); if (inode) { error = security_inode_init_security(inode, dir, NULL, shmem_initxattrs, NULL); if (error && error != -EOPNOTSUPP) goto out_iput; error = simple_acl_create(dir, inode); if (error) goto out_iput; d_tmpfile(dentry, inode); } return error; out_iput: iput(inode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro9985.34%150.00%
christoph hellwigchristoph hellwig1714.66%150.00%
Total116100.00%2100.00%


static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int error; if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0))) return error; inc_nlink(dir); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds4586.54%125.00%
pre-gitpre-git35.77%125.00%
dave hansendave hansen35.77%125.00%
al viroal viro11.92%125.00%
Total52100.00%4100.00%


static int shmem_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { return shmem_mknod(dir, dentry, mode | S_IFREG, 0); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1850.00%120.00%
pre-gitpre-git1438.89%120.00%
al viroal viro38.33%240.00%
trond myklebusttrond myklebust12.78%120.00%
Total36100.00%5100.00%

/* * Link a file.. */
static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(old_dentry); int ret; /* * No ordinary (disk based) filesystem counts links as inodes; * but each new link needs a new dentry, pinning lowmem, and * tmpfs dentries cannot be pruned until they are unlinked. */ ret = shmem_reserve_inode(inode->i_sb); if (ret) goto out; dir->i_size += BOGO_DIRENT_SIZE; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inc_nlink(inode); ihold(inode); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ d_instantiate(dentry, inode); out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds6464.00%225.00%
hugh dickinshugh dickins1515.00%225.00%
pavel emelianovpavel emelianov1414.00%112.50%
dave hansendave hansen33.00%112.50%
david howellsdavid howells33.00%112.50%
al viroal viro11.00%112.50%
Total100100.00%8100.00%


static int shmem_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(dentry); if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode)) shmem_free_inode(inode->i_sb); dir->i_size -= BOGO_DIRENT_SIZE; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; drop_nlink(inode); dput(dentry); /* Undo the count from "create" - this does all the work */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds2934.94%114.29%
hugh dickinshugh dickins2833.73%228.57%
pre-gitpre-git1922.89%114.29%
david howellsdavid howells33.61%114.29%
dave hansendave hansen33.61%114.29%
pavel emelianovpavel emelianov11.20%114.29%
Total83100.00%7100.00%


static int shmem_rmdir(struct inode *dir, struct dentry *dentry) { if (!simple_empty(dentry)) return -ENOTEMPTY; drop_nlink(d_inode(dentry)); drop_nlink(dir); return shmem_unlink(dir, dentry); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2244.90%116.67%
linus torvaldslinus torvalds1530.61%116.67%
dave hansendave hansen612.24%116.67%
david howellsdavid howells36.12%116.67%
sergey vlasovsergey vlasov24.08%116.67%
andrew mortonandrew morton12.04%116.67%
Total49100.00%6100.00%


static int shmem_exchange(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { bool old_is_dir = d_is_dir(old_dentry); bool new_is_dir = d_is_dir(new_dentry); if (old_dir != new_dir && old_is_dir != new_is_dir) { if (old_is_dir) { drop_nlink(old_dir); inc_nlink(new_dir); } else { drop_nlink(new_dir); inc_nlink(old_dir); } } old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = new_dir->i_mtime = d_inode(old_dentry)->i_ctime = d_inode(new_dentry)->i_ctime = CURRENT_TIME; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
miklos szeredimiklos szeredi11093.22%133.33%
david howellsdavid howells86.78%266.67%
Total118100.00%3100.00%


static int shmem_whiteout(struct inode *old_dir, struct dentry *old_dentry) { struct dentry *whiteout; int error; whiteout = d_alloc(old_dentry->d_parent, &old_dentry->d_name); if (!whiteout) return -ENOMEM; error = shmem_mknod(old_dir, whiteout, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV); dput(whiteout); if (error) return error; /* * Cheat and hash the whiteout while the old dentry is still in * place, instead of playing games with FS_RENAME_DOES_D_MOVE. * * d_lookup() will consistently find one of them at this point, * not sure which one, but that isn't even important. */ d_rehash(whiteout); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
miklos szeredimiklos szeredi83100.00%1100.00%
Total83100.00%1100.00%

/* * The VFS layer already does all the dentry stuff for rename, * we just have to decrement the usage count for the target if * it exists so that the VFS layer correctly free's it when it * gets overwritten. */
static int shmem_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { struct inode *inode = d_inode(old_dentry); int they_are_dirs = S_ISDIR(inode->i_mode); if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; if (flags & RENAME_EXCHANGE) return shmem_exchange(old_dir, old_dentry, new_dir, new_dentry); if (!simple_empty(new_dentry)) return -ENOTEMPTY; if (flags & RENAME_WHITEOUT) { int error; error = shmem_whiteout(old_dir, old_dentry); if (error) return error; } if (d_really_is_positive(new_dentry)) { (void) shmem_unlink(new_dir, new_dentry); if (they_are_dirs) { drop_nlink(d_inode(new_dentry)); drop_nlink(old_dir); } } else if (they_are_dirs) { drop_nlink(old_dir); inc_nlink(new_dir); } old_dir->i_size -= BOGO_DIRENT_SIZE; new_dir->i_size += BOGO_DIRENT_SIZE; old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = new_dir->i_mtime = inode->i_ctime = CURRENT_TIME; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
miklos szeredimiklos szeredi7434.26%430.77%
hugh dickinshugh dickins7132.87%323.08%
linus torvaldslinus torvalds2712.50%17.69%
pre-gitpre-git2511.57%17.69%
dave hansendave hansen94.17%215.38%
david howellsdavid howells94.17%17.69%
andrew mortonandrew morton10.46%17.69%
Total216100.00%13100.00%


static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int error; int len; struct inode *inode; struct page *page; struct shmem_inode_info *info; len = strlen(symname) + 1; if (len > PAGE_SIZE) return -ENAMETOOLONG; inode = shmem_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0, VM_NORESERVE); if (!inode) return -ENOSPC; error = security_inode_init_security(inode, dir, &dentry->d_name, shmem_initxattrs, NULL); if (error) { if (error != -EOPNOTSUPP) { iput(inode); return error; } error = 0; } info = SHMEM_I(inode); inode->i_size = len-1; if (len <= SHORT_SYMLINK_LEN) { inode->i_link = kmemdup(symname, len, GFP_KERNEL); if (!inode->i_link) { iput(inode); return -ENOMEM; } inode->i_op = &shmem_short_symlink_operations; } else { inode_nohighmem(inode); error = shmem_getpage(inode, 0, &page, SGP_WRITE); if (error) { iput(inode); return error; } inode->i_mapping->a_ops = &shmem_aops; inode->i_op = &shmem_symlink_inode_operations; memcpy(page_address(page), symname, len); SetPageUptodate(page); set_page_dirty(page); unlock_page(page); put_page(page); } dir->i_size += BOGO_DIRENT_SIZE; dir->i_ctime = dir->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); dget(dentry); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds10333.23%312.00%
hugh dickinshugh dickins6220.00%728.00%
stephen d. smalleystephen d. smalley3912.58%14.00%
al viroal viro3812.26%416.00%
pre-gitpre-git3210.32%14.00%
christoph rohlandchristoph rohland185.81%14.00%
eric pariseric paris61.94%28.00%
fengguang wufengguang wu41.29%14.00%
andrew mortonandrew morton30.97%28.00%
dmitriy monakhovdmitriy monakhov20.65%14.00%
kirill a. shutemovkirill a. shutemov20.65%14.00%
jarkko sakkinenjarkko sakkinen10.32%14.00%
Total310100.00%25100.00%


static void shmem_put_link(void *arg) { mark_page_accessed(arg); put_page(arg); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro20100.00%1100.00%
Total20100.00%1100.00%


static const char *shmem_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct page *page = NULL; int error; if (!dentry) { page = find_get_page(inode->i_mapping, 0); if (!page) return ERR_PTR(-ECHILD); if (!PageUptodate(page)) { put_page(page); return ERR_PTR(-ECHILD); } } else { error = shmem_getpage(inode, 0, &page, SGP_READ); if (error) return ERR_PTR(error); unlock_page(page); } set_delayed_call(done, shmem_put_link, page); return page_address(page); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro8463.64%538.46%
linus torvaldslinus torvalds1813.64%215.38%
pre-gitpre-git1410.61%17.69%
andrew mortonandrew morton107.58%323.08%
david howellsdavid howells32.27%17.69%
hugh dickinshugh dickins32.27%17.69%
Total132100.00%13100.00%

#ifdef CONFIG_TMPFS_XATTR /* * Superblocks without xattr inode operations may get some security.* xattr * support from the LSM "for free". As soon as we have any other xattrs * like ACLs, we also need to implement the security.* handlers at * filesystem level, though. */ /* * Callback for security_inode_init_security() for acquiring xattrs. */
static int shmem_initxattrs(struct inode *inode, const struct xattr *xattr_array, void *fs_info) { struct shmem_inode_info *info = SHMEM_I(inode); const struct xattr *xattr; struct simple_xattr *new_xattr; size_t len; for (xattr = xattr_array; xattr->name != NULL; xattr++) { new_xattr = simple_xattr_alloc(xattr->value, xattr->value_len); if (!new_xattr) return -ENOMEM; len = strlen(xattr->name) + 1; new_xattr->name = kmalloc(XATTR_SECURITY_PREFIX_LEN + len, GFP_KERNEL); if (!new_xattr->name) { kfree(new_xattr); return -ENOMEM; } memcpy(new_xattr->name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN); memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN, xattr->name, len); simple_xattr_list_add(&info->xattrs, new_xattr); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarkko sakkinenjarkko sakkinen16096.97%150.00%
aristeu sergio rozanski filhoaristeu sergio rozanski filho53.03%150.00%
Total165100.00%2100.00%


static int shmem_xattr_handler_get(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *name, void *buffer, size_t size) { struct shmem_inode_info *info = SHMEM_I(inode); name = xattr_full_name(handler, name); return simple_xattr_get(&info->xattrs, name, buffer, size); }

Contributors

PersonTokensPropCommitsCommitProp
eric pariseric paris3652.94%120.00%
aristeu sergio rozanski filhoaristeu sergio rozanski filho1420.59%120.00%
andreas gruenbacherandreas gruenbacher1116.18%240.00%
al viroal viro710.29%120.00%
Total68100.00%5100.00%


static int shmem_xattr_handler_set(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct shmem_inode_info *info = SHMEM_I(inode); name = xattr_full_name(handler, name); return simple_xattr_set(&info->xattrs, name, value, size, flags); }

Contributors

PersonTokensPropCommitsCommitProp
eric pariseric paris4358.11%120.00%
aristeu sergio rozanski filhoaristeu sergio rozanski filho1317.57%120.00%
andreas gruenbacherandreas gruenbacher1013.51%120.00%
al viroal viro79.46%120.00%
jarkko sakkinenjarkko sakkinen11.35%120.00%
Total74100.00%5100.00%

static const struct xattr_handler shmem_security_xattr_handler = { .prefix = XATTR_SECURITY_PREFIX, .get = shmem_xattr_handler_get, .set = shmem_xattr_handler_set, }; static const struct xattr_handler shmem_trusted_xattr_handler = { .prefix = XATTR_TRUSTED_PREFIX, .get = shmem_xattr_handler_get, .set = shmem_xattr_handler_set, }; static const struct xattr_handler *shmem_xattr_handlers[] = { #ifdef CONFIG_TMPFS_POSIX_ACL &posix_acl_access_xattr_handler, &posix_acl_default_xattr_handler, #endif &shmem_security_xattr_handler, &shmem_trusted_xattr_handler, NULL };
static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) { struct shmem_inode_info *info = SHMEM_I(d_inode(dentry)); return simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, size); }

Contributors

PersonTokensPropCommitsCommitProp
eric pariseric paris2959.18%116.67%
andreas gruenbacherandreas gruenbacher1122.45%233.33%
aristeu sergio rozanski filhoaristeu sergio rozanski filho510.20%116.67%
david howellsdavid howells36.12%116.67%
christoph hellwigchristoph hellwig12.04%116.67%
Total49100.00%6100.00%

#endif /* CONFIG_TMPFS_XATTR */ static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, .get_link = simple_get_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = shmem_listxattr, .removexattr = generic_removexattr, #endif }; static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, .get_link = shmem_get_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = shmem_listxattr, .removexattr = generic_removexattr, #endif };
static struct dentry *shmem_get_parent(struct dentry *child) { return ERR_PTR(-ESTALE); }

Contributors

PersonTokensPropCommitsCommitProp
david m. grimesdavid m. grimes20100.00%1100.00%
Total20100.00%1100.00%


static int shmem_match(struct inode *ino, void *vfh) { __u32 *fh = vfh; __u64 inum = fh[2]; inum = (inum << 32) | fh[1]; return ino->i_ino == inum && fh[0] == ino->i_generation; }

Contributors

PersonTokensPropCommitsCommitProp
david m. grimesdavid m. grimes58100.00%1100.00%
Total58100.00%1100.00%


static struct dentry *shmem_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct inode *inode; struct dentry *dentry = NULL; u64 inum; if (fh_len < 3) return NULL; inum = fid->raw[2]; inum = (inum << 32) | fid->raw[1]; inode = ilookup5(sb, (unsigned long)(inum + fid->raw[0]), shmem_match, fid->raw); if (inode) { dentry = d_find_alias(inode); iput(inode); } return dentry; }

Contributors

PersonTokensPropCommitsCommitProp
david m. grimesdavid m. grimes8066.12%133.33%
christoph hellwigchristoph hellwig2823.14%133.33%
hugh dickinshugh dickins1310.74%133.33%
Total121100.00%3100.00%


static int shmem_encode_fh(struct inode *inode, __u32 *fh, int *len, struct inode *parent) { if (*len < 3) { *len = 3; return FILEID_INVALID; } if (inode_unhashed(inode)) { /* Unfortunately insert_inode_hash is not idempotent, * so as we hash inodes here rather than at creation * time, we need a lock to ensure we only try * to do it once */ static DEFINE_SPINLOCK(lock); spin_lock(&lock); if (inode_unhashed(inode)) __insert_inode_hash(inode, inode->i_ino + inode->i_generation); spin_unlock(&lock); } fh[0] = inode->i_generation; fh[1] = inode->i_ino; fh[2] = ((__u64)inode->i_ino) >> 32; *len = 3; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
david m. grimesdavid m. grimes11688.55%120.00%
aneesh kumaraneesh kumar75.34%120.00%
al viroal viro75.34%240.00%
namjae jeonnamjae jeon10.76%120.00%
Total131100.00%5100.00%

static const struct export_operations shmem_export_ops = { .get_parent = shmem_get_parent, .encode_fh = shmem_encode_fh, .fh_to_dentry = shmem_fh_to_dentry, };
static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, bool remount) { char *this_char, *value, *rest; struct mempolicy *mpol = NULL; uid_t uid; gid_t gid; while (options != NULL) { this_char = options; for (;;) { /* * NUL-terminate this option: unfortunately, * mount options form a comma-separated list, * but mpol's nodelist may also contain commas. */ options = strchr(options, ','); if (options == NULL) break; options++; if (!isdigit(*options)) { options[-1] = '\0'; break; } } if (!*this_char) continue; if ((value = strchr(this_char,'=')) != NULL) { *value++ = 0; } else { pr_err("tmpfs: No value for mount option '%s'\n", this_char); goto error; } if (!strcmp(this_char,"size")) { unsigned long long size; size = memparse(value,&rest); if (*rest == '%') { size <<= PAGE_SHIFT; size *= totalram_pages; do_div(size, 100); rest++; } if (*rest) goto bad_val; sbinfo->max_blocks = DIV_ROUND_UP(size, PAGE_SIZE); } else if (!strcmp(this_char,"nr_blocks")) { sbinfo->max_blocks = memparse(value, &rest); if (*rest) goto bad_val; } else if (!strcmp(this_char,"nr_inodes")) { sbinfo->max_inodes = memparse(value, &rest); if (*rest) goto bad_val; } else if (!strcmp(this_char,"mode")) { if (remount) continue; sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777; if (*rest) goto bad_val; } else if (!strcmp(this_char,"uid")) { if (remount) continue; uid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; sbinfo->uid = make_kuid(current_user_ns(), uid); if (!uid_valid(sbinfo->uid)) goto bad_val; } else if (!strcmp(this_char,"gid")) { if (remount) continue; gid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; sbinfo->gid = make_kgid(current_user_ns(), gid); if (!gid_valid(sbinfo->gid)) goto bad_val; #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE } else if (!strcmp(this_char, "huge")) { int huge; huge = shmem_parse_huge(value); if (huge < 0) goto bad_val; if (!has_transparent_hugepage() && huge != SHMEM_HUGE_NEVER) goto bad_val; sbinfo->huge = huge; #endif #ifdef CONFIG_NUMA } else if (!strcmp(this_char,"mpol")) { mpol_put(mpol); mpol = NULL; if (mpol_parse_str(value, &mpol)) goto bad_val; #endif } else { pr_err("tmpfs: Bad mount option %s\n", this_char); goto error; } } sbinfo->mpol = mpol; return 0; bad_val: pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", value, this_char); error: mpol_put(mpol); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git11219.02%15.56%
linus torvaldslinus torvalds10818.34%211.11%
christoph rohlandchristoph rohland7713.07%15.56%
kirill a. shutemovkirill a. shutemov6210.53%316.67%
eric w. biedermaneric w. biederman569.51%15.56%
andrew mortonandrew morton498.32%211.11%
hugh dickinshugh dickins477.98%15.56%
greg thelengreg thelen355.94%15.56%
robin holtrobin holt254.24%15.56%
dave jonesdave jones91.53%15.56%
michael marineaumichael marineau40.68%15.56%
joe perchesjoe perches30.51%15.56%
lee schermerhornlee schermerhorn20.34%211.11%
Total589100.00%18100.00%


static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); struct shmem_sb_info config = *sbinfo; unsigned long inodes; int error = -EINVAL; config.mpol = NULL; if (shmem_parse_options(data, &config, true)) return error; spin_lock(&sbinfo->stat_lock); inodes = sbinfo->max_inodes - sbinfo->free_inodes; if (percpu_counter_compare(&sbinfo->used_blocks, config.max_blocks) > 0) goto out; if (config.max_inodes < inodes) goto out; /* * Those tests disallow limited->unlimited while any are in use; * but we must separately disallow unlimited->limited, because * in that case we have no record of how much is already in use. */ if (config.max_blocks && !sbinfo->max_blocks) goto out; if (config.max_inodes && !sbinfo->max_inodes) goto out; error = 0; sbinfo->huge = config.huge; sbinfo->max_blocks = config.max_blocks; sbinfo->max_inodes = config.max_inodes; sbinfo->free_inodes = config.max_inodes - inodes; /* * Preserve previous mempolicy unless mpol remount option was specified. */ if (config.mpol) { mpol_put(sbinfo->mpol); sbinfo->mpol = config.mpol; /* transfers initial ref */ } out: spin_unlock(&sbinfo->stat_lock); return error; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins10246.36%323.08%
linus torvaldslinus torvalds4319.55%215.38%
andrew mortonandrew morton2210.00%17.69%
greg thelengreg thelen156.82%17.69%
tim chentim chen104.55%17.69%
kirill a. shutemovkirill a. shutemov83.64%17.69%
lee schermerhornlee schermerhorn73.18%17.69%
robin holtrobin holt73.18%17.69%
brian gerstbrian gerst31.36%17.69%
david rientjesdavid rientjes31.36%17.69%
Total220100.00%13100.00%


static int shmem_show_options(struct seq_file *seq, struct dentry *root) { struct shmem_sb_info *sbinfo = SHMEM_SB(root->d_sb); if (sbinfo->max_blocks != shmem_default_max_blocks()) seq_printf(seq, ",size=%luk", sbinfo->max_blocks << (PAGE_SHIFT - 10)); if (sbinfo->max_inodes != shmem_default_max_inodes()) seq_printf(seq, ",nr_inodes=%lu", sbinfo->max_inodes); if (sbinfo->mode != (S_IRWXUGO | S_ISVTX)) seq_printf(seq, ",mode=%03ho", sbinfo->mode); if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID)) seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, sbinfo->uid)); if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID)) seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, sbinfo->gid)); #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE /* Rightly or wrongly, show huge mount option unmasked by shmem_huge */ if (sbinfo->huge) seq_printf(seq, ",huge=%s", shmem_format_huge(sbinfo->huge)); #endif shmem_show_mpol(seq, sbinfo->mpol); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton13670.47%112.50%
kirill a. shutemovkirill a. shutemov2713.99%337.50%
eric w. biedermaneric w. biederman2412.44%112.50%
al viroal viro52.59%225.00%
lee schermerhornlee schermerhorn10.52%112.50%
Total193100.00%8100.00%

#define MFD_NAME_PREFIX "memfd:" #define MFD_NAME_PREFIX_LEN (sizeof(MFD_NAME_PREFIX) - 1) #define MFD_NAME_MAX_LEN (NAME_MAX - MFD_NAME_PREFIX_LEN) #define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING) SYSCALL_DEFINE2(memfd_create, const char __user *, uname, unsigned int, flags) { struct shmem_inode_info *info; struct file *file; int fd, error; char *name; long len; if (flags & ~(unsigned int)MFD_ALL_FLAGS) return -EINVAL; /* length includes terminating zero */ len = strnlen_user(uname, MFD_NAME_MAX_LEN + 1); if (len <= 0) return -EFAULT; if (len > MFD_NAME_MAX_LEN + 1) return -EINVAL; name = kmalloc(len + MFD_NAME_PREFIX_LEN, GFP_TEMPORARY); if (!name) return -ENOMEM; strcpy(name, MFD_NAME_PREFIX); if (copy_from_user(&name[MFD_NAME_PREFIX_LEN], uname, len)) { error = -EFAULT; goto err_name; } /* terminating-zero may have changed after strnlen_user() returned */ if (name[len + MFD_NAME_PREFIX_LEN - 1]) { error = -EFAULT; goto err_name; } fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0); if (fd < 0) { error = fd; goto err_name; } file = shmem_file_setup(name, 0, VM_NORESERVE); if (IS_ERR(file)) { error = PTR_ERR(file); goto err_fd; } info = SHMEM_I(file_inode(file)); file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; file->f_flags |= O_RDWR | O_LARGEFILE; if (flags & MFD_ALLOW_SEALING) info->seals &= ~F_SEAL_SEAL; fd_install(fd, file); kfree(name); return fd; err_fd: put_unused_fd(fd); err_name: kfree(name); return error; } #endif /* CONFIG_TMPFS */
static void shmem_put_super(struct super_block *sb) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); percpu_counter_destroy(&sbinfo->used_blocks); mpol_put(sbinfo->mpol); kfree(sbinfo); sb->s_fs_info = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins4085.11%266.67%
greg thelengreg thelen714.89%133.33%
Total47100.00%3100.00%


int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct shmem_sb_info *sbinfo; int err = -ENOMEM; /* Round up to L1_CACHE_BYTES to resist false sharing */ sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info), L1_CACHE_BYTES), GFP_KERNEL); if (!sbinfo) return -ENOMEM; sbinfo->mode = S_IRWXUGO | S_ISVTX; sbinfo->uid = current_fsuid(); sbinfo->gid = current_fsgid(); sb->s_fs_info = sbinfo; #ifdef CONFIG_TMPFS /* * Per default we only allow half of the physical ram per * tmpfs instance, limiting inodes to one per page of lowmem; * but the internal instance is left unlimited. */ if (!(sb->s_flags & MS_KERNMOUNT)) { sbinfo->max_blocks = shmem_default_max_blocks(); sbinfo->max_inodes = shmem_default_max_inodes(); if (shmem_parse_options(data, sbinfo, false)) { err = -EINVAL; goto failed; } } else { sb->s_flags |= MS_NOUSER; } sb->s_export_op = &shmem_export_ops; sb->s_flags |= MS_NOSEC; #else sb->s_flags |= MS_NOUSER; #endif spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) goto failed; sbinfo->free_inodes = sbinfo->max_inodes; spin_lock_init(&sbinfo->shrinklist_lock); INIT_LIST_HEAD(&sbinfo->shrinklist); sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = TMPFS_MAGIC; sb->s_op = &shmem_ops; sb->s_time_gran = 1; #ifdef CONFIG_TMPFS_XATTR sb->s_xattr = shmem_xattr_handlers; #endif #ifdef CONFIG_TMPFS_POSIX_ACL sb->s_flags |= MS_POSIXACL; #endif inode = shmem_get_inode(sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE); if (!inode) goto failed; inode->i_uid = sbinfo->uid; inode->i_gid = sbinfo->gid; sb->s_root = d_make_root(inode); if (!sb->s_root) goto failed; return 0; failed: shmem_put_super(sb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds9727.02%26.25%
andrew mortonandrew morton8423.40%13.12%
hugh dickinshugh dickins4311.98%928.12%
al viroal viro246.69%515.62%
brian gerstbrian gerst185.01%13.12%
kirill a. shutemovkirill a. shutemov185.01%26.25%
andreas gruenbacherandreas gruenbacher164.46%13.12%
christoph rohlandchristoph rohland143.90%13.12%
pre-gitpre-git71.95%13.12%
tim chentim chen71.95%13.12%
david m. grimesdavid m. grimes71.95%13.12%
robin h. johnsonrobin h. johnson61.67%13.12%
eric pariseric paris61.67%13.12%
david howellsdavid howells41.11%13.12%
robin holtrobin holt30.84%13.12%
dmitriy monakhovdmitriy monakhov20.56%13.12%
tejun heotejun heo20.56%13.12%
pekka j enbergpekka j enberg10.28%13.12%
Total359100.00%32100.00%

static struct kmem_cache *shmem_inode_cachep;
static struct inode *shmem_alloc_inode(struct super_block *sb) { struct shmem_inode_info *info; info = kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL); if (!info) return NULL; return &info->vfs_inode; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds3687.80%133.33%
hugh dickinshugh dickins49.76%133.33%
christoph lameterchristoph lameter12.44%133.33%
Total41100.00%3100.00%


static void shmem_destroy_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); if (S_ISLNK(inode->i_mode)) kfree(inode->i_link); kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); }

Contributors

PersonTokensPropCommitsCommitProp
nick pigginnick piggin3567.31%125.00%
al viroal viro1630.77%250.00%
hugh dickinshugh dickins11.92%125.00%
Total52100.00%4100.00%


static void shmem_destroy_inode(struct inode *inode) { if (S_ISREG(inode->i_mode)) mpol_free_shared_policy(&SHMEM_I(inode)->policy); call_rcu(&inode->i_rcu, shmem_destroy_callback); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1536.59%116.67%
andrew mortonandrew morton1126.83%116.67%
andrea arcangeliandrea arcangeli614.63%116.67%
nick pigginnick piggin512.20%116.67%
al viroal viro37.32%116.67%
hugh dickinshugh dickins12.44%116.67%
Total41100.00%6100.00%


static void shmem_init_inode(void *foo) { struct shmem_inode_info *info = foo; inode_init_once(&info->vfs_inode); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1976.00%133.33%
christoph lameterchristoph lameter312.00%133.33%
hugh dickinshugh dickins312.00%133.33%
Total25100.00%3100.00%


static int shmem_init_inodecache(void) { shmem_inode_cachep = kmem_cache_create("shmem_inode_cache", sizeof(struct shmem_inode_info), 0, SLAB_PANIC|SLAB_ACCOUNT, shmem_init_inode); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds2784.38%125.00%
vladimir davydovvladimir davydov26.25%125.00%
hugh dickinshugh dickins26.25%125.00%
alexey dobriyanalexey dobriyan13.12%125.00%
Total32100.00%4100.00%


static void shmem_destroy_inodecache(void) { kmem_cache_destroy(shmem_inode_cachep); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1292.31%150.00%
hugh dickinshugh dickins17.69%150.00%
Total13100.00%2100.00%

static const struct address_space_operations shmem_aops = { .writepage = shmem_writepage, .set_page_dirty = __set_page_dirty_no_writeback, #ifdef CONFIG_TMPFS .write_begin = shmem_write_begin, .write_end = shmem_write_end, #endif #ifdef CONFIG_MIGRATION .migratepage = migrate_page, #endif .error_remove_page = generic_error_remove_page, }; static const struct file_operations shmem_file_operations = { .mmap = shmem_mmap, .get_unmapped_area = shmem_get_unmapped_area, #ifdef CONFIG_TMPFS .llseek = shmem_file_llseek, .read_iter = shmem_file_read_iter, .write_iter = generic_file_write_iter, .fsync = noop_fsync, .splice_read = shmem_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = shmem_fallocate, #endif }; static const struct inode_operations shmem_inode_operations = { .getattr = shmem_getattr, .setattr = shmem_setattr, #ifdef CONFIG_TMPFS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = shmem_listxattr, .removexattr = generic_removexattr, .set_acl = simple_set_acl, #endif }; static const struct inode_operations shmem_dir_inode_operations = { #ifdef CONFIG_TMPFS .create = shmem_create, .lookup = simple_lookup, .link = shmem_link, .unlink = shmem_unlink, .symlink = shmem_symlink, .mkdir = shmem_mkdir, .rmdir = shmem_rmdir, .mknod = shmem_mknod, .rename2 = shmem_rename2, .tmpfile = shmem_tmpfile, #endif #ifdef CONFIG_TMPFS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = shmem_listxattr, .removexattr = generic_removexattr, #endif #ifdef CONFIG_TMPFS_POSIX_ACL .setattr = shmem_setattr, .set_acl = simple_set_acl, #endif }; static const struct inode_operations shmem_special_inode_operations = { #ifdef CONFIG_TMPFS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = shmem_listxattr, .removexattr = generic_removexattr, #endif #ifdef CONFIG_TMPFS_POSIX_ACL .setattr = shmem_setattr, .set_acl = simple_set_acl, #endif }; static const struct super_operations shmem_ops = { .alloc_inode = shmem_alloc_inode, .destroy_inode = shmem_destroy_inode, #ifdef CONFIG_TMPFS .statfs = shmem_statfs, .remount_fs = shmem_remount_fs, .show_options = shmem_show_options, #endif .evict_inode = shmem_evict_inode, .drop_inode = generic_delete_inode, .put_super = shmem_put_super, #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE .nr_cached_objects = shmem_unused_huge_count, .free_cached_objects = shmem_unused_huge_scan, #endif }; static const struct vm_operations_struct shmem_vm_ops = { .fault = shmem_fault, .map_pages = filemap_map_pages, #ifdef CONFIG_NUMA .set_policy = shmem_set_policy, .get_policy = shmem_get_policy, #endif };
static struct dentry *shmem_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, shmem_fill_super); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro3697.30%266.67%
andries brouwerandries brouwer12.70%133.33%
Total37100.00%3100.00%

static struct file_system_type shmem_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", .mount = shmem_mount, .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, };
int __init shmem_init(void) { int error; /* If rootfs called this, don't re-init */ if (shmem_inode_cachep) return 0; error = shmem_init_inodecache(); if (error) goto out3; error = register_filesystem(&shmem_fs_type); if (error) { pr_err("Could not register tmpfs\n"); goto out2; } shm_mnt = kern_mount(&shmem_fs_type); if (IS_ERR(shm_mnt)) { error = PTR_ERR(shm_mnt); pr_err("Could not kern_mount tmpfs\n"); goto out1; } #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE if (has_transparent_hugepage() && shmem_huge < SHMEM_HUGE_DENY) SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge; else shmem_huge = 0; /* just in case it was patched */ #endif return 0; out1: unregister_filesystem(&shmem_fs_type); out2: shmem_destroy_inodecache(); out3: shm_mnt = ERR_PTR(error); return error; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds5840.28%218.18%
kirill a. shutemovkirill a. shutemov3121.53%218.18%
pre-gitpre-git2517.36%19.09%
andrew mortonandrew morton128.33%19.09%
rob landleyrob landley85.56%19.09%
hugh dickinshugh dickins64.17%19.09%
joe perchesjoe perches21.39%19.09%
al viroal viro10.69%19.09%
trond myklebusttrond myklebust10.69%19.09%
Total144100.00%11100.00%

#if defined(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && defined(CONFIG_SYSFS)
static ssize_t shmem_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int values[] = { SHMEM_HUGE_ALWAYS, SHMEM_HUGE_WITHIN_SIZE, SHMEM_HUGE_ADVISE, SHMEM_HUGE_NEVER, SHMEM_HUGE_DENY, SHMEM_HUGE_FORCE, }; int i, count; for (i = 0, count = 0; i < ARRAY_SIZE(values); i++) { const char *fmt = shmem_huge == values[i] ? "[%s] " : "%s "; count += sprintf(buf + count, fmt, shmem_format_huge(values[i])); } buf[count - 1] = '\n'; return count; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov112100.00%1100.00%
Total112100.00%1100.00%


static ssize_t shmem_enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { char tmp[16]; int huge; if (count + 1 > sizeof(tmp)) return -EINVAL; memcpy(tmp, buf, count); tmp[count] = '\0'; if (count && tmp[count - 1] == '\n') tmp[count - 1] = '\0'; huge = shmem_parse_huge(tmp); if (huge == -EINVAL) return -EINVAL; if (!has_transparent_hugepage() && huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY) return -EINVAL; shmem_huge = huge; if (shmem_huge < SHMEM_HUGE_DENY) SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge; return count; }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov146100.00%1100.00%
Total146100.00%1100.00%

struct kobj_attribute shmem_enabled_attr = __ATTR(shmem_enabled, 0644, shmem_enabled_show, shmem_enabled_store); #endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE && CONFIG_SYSFS */ #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
bool shmem_huge_enabled(struct vm_area_struct *vma) { struct inode *inode = file_inode(vma->vm_file); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); loff_t i_size; pgoff_t off; if (shmem_huge == SHMEM_HUGE_FORCE) return true; if (shmem_huge == SHMEM_HUGE_DENY) return false; switch (sbinfo->huge) { case SHMEM_HUGE_NEVER: return false; case SHMEM_HUGE_ALWAYS: return true; case SHMEM_HUGE_WITHIN_SIZE: off = round_up(vma->vm_pgoff, HPAGE_PMD_NR); i_size = round_up(i_size_read(inode), PAGE_SIZE); if (i_size >= HPAGE_PMD_SIZE && i_size >> PAGE_SHIFT >= off) return true; case SHMEM_HUGE_ADVISE: /* TODO: implement fadvise() hints */ return (vma->vm_flags & VM_HUGEPAGE); default: VM_BUG_ON(1); return false; } }

Contributors

PersonTokensPropCommitsCommitProp
kirill a. shutemovkirill a. shutemov141100.00%1100.00%
Total141100.00%1100.00%

#endif /* CONFIG_TRANSPARENT_HUGE_PAGECACHE */ #else /* !CONFIG_SHMEM */ /* * tiny-shmem: simple shmemfs and tmpfs using ramfs code * * This is intended for small system where the benefits of the full * shmem code (swap-backed and resource-limited) are outweighed by * their complexity. On systems without swap this code should be * effectively equivalent, but much lighter weight. */ static struct file_system_type shmem_fs_type = { .name = "tmpfs", .mount = ramfs_mount, .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, };
int __init shmem_init(void) { BUG_ON(register_filesystem(&shmem_fs_type) != 0); shm_mnt = kern_mount(&shmem_fs_type); BUG_ON(IS_ERR(shm_mnt)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
matt mackallmatt mackall3386.84%133.33%
hugh dickinshugh dickins37.89%133.33%
pre-gitpre-git25.26%133.33%
Total38100.00%3100.00%


int shmem_unuse(swp_entry_t swap, struct page *page) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
matt mackallmatt mackall1593.75%150.00%
hugh dickinshugh dickins16.25%150.00%
Total16100.00%2100.00%


int shmem_lock(struct file *file, int lock, struct user_struct *user) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins21100.00%1100.00%
Total21100.00%1100.00%


void shmem_unlock_mapping(struct address_space *mapping) { }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins9100.00%1100.00%
Total9100.00%1100.00%

#ifdef CONFIG_MMU
unsigned long shmem_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { return current->mm->get_unmapped_area(file, addr, len, pgoff, flags); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins45100.00%1100.00%
Total45100.00%1100.00%

#endif
void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) { truncate_inode_pages_range(inode->i_mapping, lstart, lend); }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins27100.00%2100.00%
Total27100.00%2100.00%

EXPORT_SYMBOL_GPL(shmem_truncate_range); #define shmem_vm_ops generic_file_vm_ops #define shmem_file_operations ramfs_file_operations #define shmem_get_inode(sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev) #define shmem_acct_size(flags, size) 0 #define shmem_unacct_size(flags, size) do {} while (0) #endif /* CONFIG_SHMEM */ /* common code */ static struct dentry_operations anon_ops = { .d_dname = simple_dname };
static struct file *__shmem_file_setup(const char *name, loff_t size, unsigned long flags, unsigned int i_flags) { struct file *res; struct inode *inode; struct path path; struct super_block *sb; struct qstr this; if (IS_ERR(shm_mnt)) return ERR_CAST(shm_mnt); if (size < 0 || size > MAX_LFS_FILESIZE) return ERR_PTR(-EINVAL); if (shmem_acct_size(flags, size)) return ERR_PTR(-ENOMEM); res = ERR_PTR(-ENOMEM); this.name = name; this.len = strlen(name); this.hash = 0; /* will go */ sb = shm_mnt->mnt_sb; path.mnt = mntget(shm_mnt); path.dentry = d_alloc_pseudo(sb, &this); if (!path.dentry) goto put_memory; d_set_d_op(path.dentry, &anon_ops); res = ERR_PTR(-ENOSPC); inode = shmem_get_inode(sb, NULL, S_IFREG | S_IRWXUGO, 0, flags); if (!inode) goto put_memory; inode->i_flags |= i_flags; d_instantiate(path.dentry, inode); inode->i_size = size; clear_nlink(inode); /* It is unlinked */ res = ERR_PTR(ramfs_nommu_expand_for_mapping(inode, size)); if (IS_ERR(res)) goto put_path; res = alloc_file(&path, FMODE_WRITE | FMODE_READ, &shmem_file_operations); if (IS_ERR(res)) goto put_path; return res; put_memory: shmem_unacct_size(flags, size); put_path: path_put(&path); return res; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git10936.33%14.00%
al viroal viro7123.67%520.00%
andrew mortonandrew morton237.67%312.00%
hugh dickinshugh dickins217.00%520.00%
linus torvaldslinus torvalds217.00%312.00%
konstantin khlebnikovkonstantin khlebnikov206.67%14.00%
matt mackallmatt mackall124.00%14.00%
eric pariseric paris124.00%14.00%
miklos szeredimiklos szeredi31.00%14.00%
anatol pomozovanatol pomozov31.00%14.00%
dmitriy monakhovdmitriy monakhov20.67%14.00%
mimi zoharmimi zohar20.67%14.00%
sergei trofimovichsergei trofimovich10.33%14.00%
Total300100.00%25100.00%

/** * 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 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 */
struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags) { return __shmem_file_setup(name, size, flags, S_PRIVATE); }

Contributors

PersonTokensPropCommitsCommitProp
eric pariseric paris31100.00%1100.00%
Total31100.00%1100.00%

/** * shmem_file_setup - get an unlinked file living in tmpfs * @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 */
struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags) { return __shmem_file_setup(name, size, flags, 0); }

Contributors

PersonTokensPropCommitsCommitProp
eric pariseric paris31100.00%1100.00%
Total31100.00%1100.00%

EXPORT_SYMBOL_GPL(shmem_file_setup); /** * shmem_zero_setup - setup a shared anonymous mapping * @vma: the vma to be mmapped is prepared by do_mmap_pgoff */
int shmem_zero_setup(struct vm_area_struct *vma) { struct file *file; loff_t size = vma->vm_end - vma->vm_start; /* * Cloning a new file under mmap_sem leads to a lock ordering conflict * between XFS directory reading and selinux: since this file is only * accessible to the user through its mapping, use S_PRIVATE flag to * bypass file security, in the same way as shmem_kernel_file_setup(). */ file = __shmem_file_setup("dev/zero", size, vma->vm_flags, S_PRIVATE); if (IS_ERR(file)) return PTR_ERR(file); if (vma->vm_file) fput(vma->vm_file); vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && ((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) < (vma->vm_end & HPAGE_PMD_MASK)) { khugepaged_enter(vma, vma->vm_flags); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git7560.98%116.67%
kirill a. shutemovkirill a. shutemov3931.71%233.33%
hugh dickinshugh dickins86.50%233.33%
linus torvaldslinus torvalds10.81%116.67%
Total123100.00%6100.00%

/** * shmem_read_mapping_page_gfp - read into page cache, using specified page allocation flags. * @mapping: the page's address_space * @index: the page index * @gfp: the page allocator flags to use if allocating * * This behaves as a tmpfs "read_cache_page_gfp(mapping, index, gfp)", * with any new page allocations done using the specified allocation flags. * But read_cache_page_gfp() uses the ->readpage() method: which does not * suit tmpfs, since it may have pages in swapcache, and needs to find those * for itself; although drivers/gpu/drm i915 and ttm rely upon this support. * * i915_gem_object_get_pages_gtt() mixes __GFP_NORETRY | __GFP_NOWARN in * with the mapping_gfp_mask(), to avoid OOMing the machine unnecessarily. */
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp) { #ifdef CONFIG_SHMEM struct inode *inode = mapping->host; struct page *page; int error; BUG_ON(mapping->a_ops != &shmem_aops); error = shmem_getpage_gfp(inode, index, &page, SGP_CACHE, gfp, NULL, NULL); if (error) page = ERR_PTR(error); else unlock_page(page); return page; #else /* * The tiny !SHMEM case uses ramfs without swap */ return read_cache_page_gfp(mapping, index, gfp); #endif }

Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins10198.06%266.67%
andres lagar-cavillaandres lagar-cavilla21.94%133.33%
Total103100.00%3100.00%

EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);

Overall Contributors

PersonTokensPropCommitsCommitProp
hugh dickinshugh dickins650235.02%10929.70%
kirill a. shutemovkirill a. shutemov335618.07%102.72%
linus torvaldslinus torvalds16158.70%154.09%
david herrmanndavid herrmann11626.26%30.82%
andrew mortonandrew morton10465.63%3710.08%
pre-gitpre-git6323.40%10.27%
al viroal viro5923.19%5113.90%
david m. grimesdavid m. grimes3081.66%10.27%
eric pariseric paris2881.55%30.82%
miklos szeredimiklos szeredi2721.46%51.36%
andreas gruenbacherandreas gruenbacher2691.45%30.82%
vlastimil babkavlastimil babka2221.20%20.54%
jarkko sakkinenjarkko sakkinen1911.03%10.27%
nick pigginnick piggin1911.03%92.45%
pavel emelianovpavel emelianov1440.78%10.27%
johannes weinerjohannes weiner1390.75%41.09%
christoph hellwigchristoph hellwig1310.71%143.81%
konstantin khlebnikovkonstantin khlebnikov1300.70%20.54%
matt mackallmatt mackall1290.69%10.27%
lee schermerhornlee schermerhorn1250.67%41.09%
christoph rohlandchristoph rohland1090.59%10.27%
eric w. biedermaneric w. biederman900.48%20.54%
yu zhaoyu zhao740.40%10.27%
rik van rielrik van riel730.39%10.27%
andres lagar-cavillaandres lagar-cavilla650.35%10.27%
greg thelengreg thelen570.31%20.54%
stephen d. smalleystephen d. smalley520.28%20.54%
matthew wilcoxmatthew wilcox480.26%20.54%
aristeu sergio rozanski filhoaristeu sergio rozanski filho470.25%10.27%
david howellsdavid howells440.24%41.09%
brian gerstbrian gerst420.23%20.54%
robin holtrobin holt390.21%10.27%
mel gormanmel gorman380.20%30.82%
tim chentim chen330.18%10.27%
dave hansendave hansen270.15%20.54%
dmitriy monakhovdmitriy monakhov230.12%10.27%
andrea arcangeliandrea arcangeli230.12%10.27%
sasha levinsasha levin220.12%20.54%
mimi zoharmimi zohar200.11%30.82%
vladimir davydovvladimir davydov170.09%30.82%
li shaohuali shaohua160.09%10.27%
geert uytterhoevengeert uytterhoeven110.06%20.54%
dave jonesdave jones90.05%10.27%
arjan van de venarjan van de ven80.04%10.27%
rob landleyrob landley80.04%10.27%
jeff liujeff liu70.04%10.27%
arnd bergmannarnd bergmann70.04%10.27%
aneesh kumaraneesh kumar70.04%10.27%
christoph lameterchristoph lameter70.04%30.82%
fengguang wufengguang wu60.03%10.27%
robin h. johnsonrobin h. johnson60.03%10.27%
keith packardkeith packard50.03%10.27%
joe perchesjoe perches50.03%10.27%
eric dumazeteric dumazet50.03%10.27%
adrian bunkadrian bunk50.03%30.82%
andi kleenandi kleen50.03%10.27%
nathan zimmernathan zimmer50.03%10.27%
ning quning qu50.03%10.27%
michael marineaumichael marineau40.02%10.27%
anthony romanoanthony romano40.02%10.27%
andries brouwerandries brouwer40.02%20.54%
thomas gleixnerthomas gleixner30.02%10.27%
kamezawa hiroyukikamezawa hiroyuki30.02%20.54%
anatol pomozovanatol pomozov30.02%10.27%
alexey dobriyanalexey dobriyan30.02%20.54%
pekka j enbergpekka j enberg30.02%20.54%
david rientjesdavid rientjes30.02%10.27%
tejun heotejun heo20.01%10.27%
kent overstreetkent overstreet20.01%10.27%
sergey vlasovsergey vlasov20.01%10.27%
trond myklebusttrond myklebust20.01%20.54%
ingo molnaringo molnar20.01%10.27%
stephen hemmingerstephen hemminger20.01%10.27%
helge dellerhelge deller20.01%10.27%
jes sorensenjes sorensen10.01%10.27%
toshi kanitoshi kani10.01%10.27%
namjae jeonnamjae jeon10.01%10.27%
randy dunlaprandy dunlap10.01%10.27%
jan karajan kara10.01%10.27%
josef bacikjosef bacik10.01%10.27%
sergei trofimovichsergei trofimovich10.01%10.27%
ken chenken chen10.01%10.27%
badari pulavartybadari pulavarty10.01%10.27%
zach brownzach brown10.01%10.27%
paul gortmakerpaul gortmaker10.01%10.27%
Total18569100.00%367100.00%
Directory: mm
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.