Merge branch 'akpm' (patches from Andrew)
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Nov 2021 18:11:53 +0000 (10:11 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Nov 2021 18:11:53 +0000 (10:11 -0800)
Merge more updates from Andrew Morton:
 "87 patches.

  Subsystems affected by this patch series: mm (pagecache and hugetlb),
  procfs, misc, MAINTAINERS, lib, checkpatch, binfmt, kallsyms, ramfs,
  init, codafs, nilfs2, hfs, crash_dump, signals, seq_file, fork,
  sysvfs, kcov, gdb, resource, selftests, and ipc"

* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (87 commits)
  ipc/ipc_sysctl.c: remove fallback for !CONFIG_PROC_SYSCTL
  ipc: check checkpoint_restore_ns_capable() to modify C/R proc files
  selftests/kselftest/runner/run_one(): allow running non-executable files
  virtio-mem: disallow mapping virtio-mem memory via /dev/mem
  kernel/resource: disallow access to exclusive system RAM regions
  kernel/resource: clean up and optimize iomem_is_exclusive()
  scripts/gdb: handle split debug for vmlinux
  kcov: replace local_irq_save() with a local_lock_t
  kcov: avoid enable+disable interrupts if !in_task()
  kcov: allocate per-CPU memory on the relevant node
  Documentation/kcov: define `ip' in the example
  Documentation/kcov: include types.h in the example
  sysv: use BUILD_BUG_ON instead of runtime check
  kernel/fork.c: unshare(): use swap() to make code cleaner
  seq_file: fix passing wrong private data
  seq_file: move seq_escape() to a header
  signal: remove duplicate include in signal.h
  crash_dump: remove duplicate include in crash_dump.h
  crash_dump: fix boolreturn.cocci warning
  hfs/hfsplus: use WARN_ON for sanity check
  ...

131 files changed:
.mailmap
Documentation/dev-tools/kcov.rst
MAINTAINERS
arch/alpha/kernel/traps.c
arch/microblaze/mm/pgtable.c
arch/powerpc/mm/pgtable_32.c
arch/riscv/lib/delay.c
arch/s390/include/asm/facility.h
arch/x86/kernel/aperture_64.c
arch/x86/kernel/unwind_orc.c
arch/x86/mm/init_32.c
arch/x86/xen/mmu_hvm.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/i915/i915_vma.c
drivers/gpu/drm/i915/intel_runtime_pm.c
drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
drivers/virtio/Kconfig
drivers/virtio/virtio_mem.c
fs/binfmt_elf.c
fs/coda/cnode.c
fs/coda/coda_linux.c
fs/coda/coda_linux.h
fs/coda/dir.c
fs/coda/file.c
fs/coda/psdev.c
fs/coda/upcall.c
fs/hfs/inode.c
fs/hfsplus/inode.c
fs/hugetlbfs/inode.c
fs/inode.c
fs/internal.h
fs/nilfs2/alloc.c
fs/nilfs2/alloc.h
fs/nilfs2/bmap.c
fs/nilfs2/bmap.h
fs/nilfs2/btnode.c
fs/nilfs2/btnode.h
fs/nilfs2/btree.c
fs/nilfs2/btree.h
fs/nilfs2/cpfile.c
fs/nilfs2/cpfile.h
fs/nilfs2/dat.c
fs/nilfs2/dat.h
fs/nilfs2/dir.c
fs/nilfs2/direct.c
fs/nilfs2/direct.h
fs/nilfs2/file.c
fs/nilfs2/gcinode.c
fs/nilfs2/ifile.c
fs/nilfs2/ifile.h
fs/nilfs2/inode.c
fs/nilfs2/ioctl.c
fs/nilfs2/mdt.c
fs/nilfs2/mdt.h
fs/nilfs2/namei.c
fs/nilfs2/nilfs.h
fs/nilfs2/page.c
fs/nilfs2/page.h
fs/nilfs2/recovery.c
fs/nilfs2/segbuf.c
fs/nilfs2/segbuf.h
fs/nilfs2/segment.c
fs/nilfs2/segment.h
fs/nilfs2/sufile.c
fs/nilfs2/sufile.h
fs/nilfs2/super.c
fs/nilfs2/sysfs.c
fs/nilfs2/sysfs.h
fs/nilfs2/the_nilfs.c
fs/nilfs2/the_nilfs.h
fs/proc/base.c
fs/proc/vmcore.c
fs/ramfs/inode.c
fs/seq_file.c
fs/sysv/super.c
include/asm-generic/sections.h
include/kunit/test.h
include/linux/bottom_half.h
include/linux/container_of.h [new file with mode: 0644]
include/linux/crash_dump.h
include/linux/delay.h
include/linux/fs.h
include/linux/generic-radix-tree.h
include/linux/hugetlb.h
include/linux/instruction_pointer.h [new file with mode: 0644]
include/linux/kallsyms.h
include/linux/kernel.h
include/linux/list.h
include/linux/llist.h
include/linux/pagemap.h
include/linux/plist.h
include/linux/radix-tree.h
include/linux/rwsem.h
include/linux/sbitmap.h
include/linux/seq_file.h
include/linux/signal.h
include/linux/smp.h
include/linux/spinlock.h
include/linux/stackdepot.h
include/linux/string_helpers.h
include/media/media-entity.h
init/main.c
ipc/ipc_sysctl.c
ipc/shm.c
kernel/extable.c
kernel/fork.c
kernel/kcov.c
kernel/locking/lockdep.c
kernel/resource.c
kernel/trace/ftrace.c
lib/scatterlist.c
lib/stackdepot.c
lib/vsprintf.c
mm/Kconfig
mm/filemap.c
mm/kasan/report.c
mm/memfd.c
mm/mmap.c
mm/page_owner.c
mm/truncate.c
mm/vmscan.c
mm/workingset.c
net/sysctl_net.c
scripts/checkpatch.pl
scripts/const_structs.checkpatch
scripts/gdb/linux/symbols.py
tools/testing/selftests/kselftest/runner.sh
tools/testing/selftests/proc/.gitignore
tools/testing/selftests/proc/Makefile
tools/testing/selftests/proc/proc-tid0.c [new file with mode: 0644]

index 9d4fc1f..14314e3 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -73,6 +73,8 @@ Chris Chiu <chris.chiu@canonical.com> <chiu@endlessm.com>
 Chris Chiu <chris.chiu@canonical.com> <chiu@endlessos.org>
 Christophe Ricard <christophe.ricard@gmail.com>
 Christoph Hellwig <hch@lst.de>
+Colin Ian King <colin.king@intel.com> <colin.king@canonical.com>
+Colin Ian King <colin.king@intel.com> <colin.i.king@gmail.com>
 Corey Minyard <minyard@acm.org>
 Damian Hobson-Garcia <dhobsong@igel.co.jp>
 Daniel Borkmann <daniel@iogearbox.net> <danborkmann@googlemail.com>
index d2c4c27..d83c9ab 100644 (file)
@@ -50,6 +50,7 @@ program using kcov:
     #include <sys/mman.h>
     #include <unistd.h>
     #include <fcntl.h>
+    #include <linux/types.h>
 
     #define KCOV_INIT_TRACE                    _IOR('c', 1, unsigned long)
     #define KCOV_ENABLE                        _IO('c', 100)
@@ -177,6 +178,8 @@ Comparison operands collection is similar to coverage collection:
        /* Read number of comparisons collected. */
        n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
        for (i = 0; i < n; i++) {
+               uint64_t ip;
+
                type = cover[i * KCOV_WORDS_PER_CMP + 1];
                /* arg1 and arg2 - operands of the comparison. */
                arg1 = cover[i * KCOV_WORDS_PER_CMP + 2];
@@ -251,6 +254,8 @@ selectively from different subsystems.
 
 .. code-block:: c
 
+    /* Same includes and defines as above. */
+
     struct kcov_remote_arg {
        __u32           trace_mode;
        __u32           area_size;
index 170bbbe..a2bd991 100644 (file)
@@ -767,7 +767,7 @@ F:  drivers/crypto/allwinner/
 ALLWINNER HARDWARE SPINLOCK SUPPORT
 M:     Wilken Gottwalt <wilken.gottwalt@posteo.net>
 S:     Maintained
-F:     Documentation/devicetree/bindings/hwlock/allwinner,sun6i-hwspinlock.yaml
+F:     Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml
 F:     drivers/hwspinlock/sun6i_hwspinlock.c
 
 ALLWINNER THERMAL DRIVER
@@ -2783,7 +2783,7 @@ F:        Documentation/devicetree/bindings/arm/toshiba.yaml
 F:     Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml
 F:     Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml
 F:     Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml
-F:     Documentation/devicetree/bindings/pinctrl/toshiba,tmpv7700-pinctrl.yaml
+F:     Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml
 F:     Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml
 F:     arch/arm64/boot/dts/toshiba/
 F:     drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
@@ -7119,6 +7119,20 @@ F:       include/uapi/linux/mdio.h
 F:     include/uapi/linux/mii.h
 F:     net/core/of_net.c
 
+EXEC & BINFMT API
+R:     Eric Biederman <ebiederm@xmission.com>
+R:     Kees Cook <keescook@chromium.org>
+F:     arch/alpha/kernel/binfmt_loader.c
+F:     arch/x86/ia32/ia32_aout.c
+F:     fs/*binfmt_*.c
+F:     fs/exec.c
+F:     include/linux/binfmts.h
+F:     include/linux/elf.h
+F:     include/uapi/linux/binfmts.h
+F:     tools/testing/selftests/exec/
+N:     asm/elf.h
+N:     binfmt
+
 EXFAT FILE SYSTEM
 M:     Namjae Jeon <linkinjeon@kernel.org>
 M:     Sungjong Seo <sj1557.seo@samsung.com>
@@ -8562,7 +8576,6 @@ M:        John Stultz <john.stultz@linaro.org>
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 F:     drivers/misc/hisi_hikey_usb.c
-F:     Documentation/devicetree/bindings/misc/hisilicon-hikey-usb.yaml
 
 HISILICON PMU DRIVER
 M:     Shaokun Zhang <zhangshaokun@hisilicon.com>
@@ -9621,7 +9634,7 @@ INTEL KEEM BAY DRM DRIVER
 M:     Anitha Chrisanthus <anitha.chrisanthus@intel.com>
 M:     Edmund Dea <edmund.j.dea@intel.com>
 S:     Maintained
-F:     Documentation/devicetree/bindings/display/intel,kmb_display.yaml
+F:     Documentation/devicetree/bindings/display/intel,keembay-display.yaml
 F:     drivers/gpu/drm/kmb/
 
 INTEL KEEM BAY OCS AES/SM4 CRYPTO DRIVER
index e805106..2ae3470 100644 (file)
@@ -129,9 +129,7 @@ dik_show_trace(unsigned long *sp, const char *loglvl)
                extern char _stext[], _etext[];
                unsigned long tmp = *sp;
                sp++;
-               if (tmp < (unsigned long) &_stext)
-                       continue;
-               if (tmp >= (unsigned long) &_etext)
+               if (!is_kernel_text(tmp))
                        continue;
                printk("%s[<%lx>] %pSR\n", loglvl, tmp, (void *)tmp);
                if (i > 40) {
index c1833b1..9f73265 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/mm_types.h>
 #include <linux/pgtable.h>
 #include <linux/memblock.h>
+#include <linux/kallsyms.h>
 
 #include <asm/pgalloc.h>
 #include <linux/io.h>
@@ -171,7 +172,7 @@ void __init mapin_ram(void)
        for (s = 0; s < lowmem_size; s += PAGE_SIZE) {
                f = _PAGE_PRESENT | _PAGE_ACCESSED |
                                _PAGE_SHARED | _PAGE_HWEXEC;
-               if ((char *) v < _stext || (char *) v >= _etext)
+               if (!is_kernel_text(v))
                        f |= _PAGE_WRENABLE;
                else
                        /* On the MicroBlaze, no user access
index fde1ed4..906e4e4 100644 (file)
@@ -33,8 +33,6 @@
 
 #include <mm/mmu_decl.h>
 
-extern char etext[], _stext[], _sinittext[], _einittext[];
-
 static u8 early_fixmap_pagetable[FIXMAP_PTE_SIZE] __page_aligned_data;
 
 notrace void __init early_ioremap_init(void)
@@ -104,14 +102,13 @@ static void __init __mapin_ram_chunk(unsigned long offset, unsigned long top)
 {
        unsigned long v, s;
        phys_addr_t p;
-       int ktext;
+       bool ktext;
 
        s = offset;
        v = PAGE_OFFSET + s;
        p = memstart_addr + s;
        for (; s < top; s += PAGE_SIZE) {
-               ktext = ((char *)v >= _stext && (char *)v < etext) ||
-                       ((char *)v >= _sinittext && (char *)v < _einittext);
+               ktext = core_kernel_text(v);
                map_kernel_page(v, p, ktext ? PAGE_KERNEL_TEXT : PAGE_KERNEL);
                v += PAGE_SIZE;
                p += PAGE_SIZE;
index f51c9a0..49d510b 100644 (file)
@@ -4,10 +4,14 @@
  */
 
 #include <linux/delay.h>
+#include <linux/math.h>
 #include <linux/param.h>
 #include <linux/timex.h>
+#include <linux/types.h>
 #include <linux/export.h>
 
+#include <asm/processor.h>
+
 /*
  * This is copies from arch/arm/include/asm/delay.h
  *
index e3aa354..94b6919 100644 (file)
@@ -9,8 +9,12 @@
 #define __ASM_FACILITY_H
 
 #include <asm/facility-defs.h>
+
+#include <linux/minmax.h>
 #include <linux/string.h>
+#include <linux/types.h>
 #include <linux/preempt.h>
+
 #include <asm/lowcore.h>
 
 #define MAX_FACILITY_BIT (sizeof(stfle_fac_list) * 8)
index 1056288..af3ba08 100644 (file)
@@ -73,12 +73,23 @@ static int gart_mem_pfn_is_ram(unsigned long pfn)
                      (pfn >= aperture_pfn_start + aperture_page_count));
 }
 
+#ifdef CONFIG_PROC_VMCORE
+static bool gart_oldmem_pfn_is_ram(struct vmcore_cb *cb, unsigned long pfn)
+{
+       return !!gart_mem_pfn_is_ram(pfn);
+}
+
+static struct vmcore_cb gart_vmcore_cb = {
+       .pfn_is_ram = gart_oldmem_pfn_is_ram,
+};
+#endif
+
 static void __init exclude_from_core(u64 aper_base, u32 aper_order)
 {
        aperture_pfn_start = aper_base >> PAGE_SHIFT;
        aperture_page_count = (32 * 1024 * 1024) << aper_order >> PAGE_SHIFT;
 #ifdef CONFIG_PROC_VMCORE
-       WARN_ON(register_oldmem_pfn_is_ram(&gart_mem_pfn_is_ram));
+       register_vmcore_cb(&gart_vmcore_cb);
 #endif
 #ifdef CONFIG_PROC_KCORE
        WARN_ON(register_mem_pfn_is_ram(&gart_mem_pfn_is_ram));
index e6f7592..2de3c8c 100644 (file)
@@ -175,7 +175,7 @@ static struct orc_entry *orc_find(unsigned long ip)
        }
 
        /* vmlinux .init slow lookup: */
-       if (init_kernel_text(ip))
+       if (is_kernel_inittext(ip))
                return __orc_find(__start_orc_unwind_ip, __start_orc_unwind,
                                  __stop_orc_unwind_ip - __start_orc_unwind_ip, ip);
 
index 5cd7ea6..d4e2648 100644 (file)
@@ -238,11 +238,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
        }
 }
 
-/*
- * The <linux/kallsyms.h> already defines is_kernel_text,
- * using '__' prefix not to get in conflict.
- */
-static inline int __is_kernel_text(unsigned long addr)
+static inline int is_x86_32_kernel_text(unsigned long addr)
 {
        if (addr >= (unsigned long)_text && addr <= (unsigned long)__init_end)
                return 1;
@@ -333,8 +329,8 @@ repeat:
                                addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE +
                                        PAGE_OFFSET + PAGE_SIZE-1;
 
-                               if (__is_kernel_text(addr) ||
-                                   __is_kernel_text(addr2))
+                               if (is_x86_32_kernel_text(addr) ||
+                                   is_x86_32_kernel_text(addr2))
                                        prot = PAGE_KERNEL_LARGE_EXEC;
 
                                pages_2m++;
@@ -359,7 +355,7 @@ repeat:
                                 */
                                pgprot_t init_prot = __pgprot(PTE_IDENT_ATTR);
 
-                               if (__is_kernel_text(addr))
+                               if (is_x86_32_kernel_text(addr))
                                        prot = PAGE_KERNEL_EXEC;
 
                                pages_4k++;
@@ -789,7 +785,7 @@ static void mark_nxdata_nx(void)
         */
        unsigned long start = PFN_ALIGN(_etext);
        /*
-        * This comes from __is_kernel_text upper limit. Also HPAGE where used:
+        * This comes from is_x86_32_kernel_text upper limit. Also HPAGE where used:
         */
        unsigned long size = (((unsigned long)__init_end + HPAGE_SIZE) & HPAGE_MASK) - start;
 
index 5740937..509bdee 100644 (file)
@@ -9,39 +9,28 @@
 
 #ifdef CONFIG_PROC_VMCORE
 /*
- * This function is used in two contexts:
- * - the kdump kernel has to check whether a pfn of the crashed kernel
- *   was a ballooned page. vmcore is using this function to decide
- *   whether to access a pfn of the crashed kernel.
- * - the kexec kernel has to check whether a pfn was ballooned by the
- *   previous kernel. If the pfn is ballooned, handle it properly.
- * Returns 0 if the pfn is not backed by a RAM page, the caller may
+ * The kdump kernel has to check whether a pfn of the crashed kernel
+ * was a ballooned page. vmcore is using this function to decide
+ * whether to access a pfn of the crashed kernel.
+ * Returns "false" if the pfn is not backed by a RAM page, the caller may
  * handle the pfn special in this case.
  */
-static int xen_oldmem_pfn_is_ram(unsigned long pfn)
+static bool xen_vmcore_pfn_is_ram(struct vmcore_cb *cb, unsigned long pfn)
 {
        struct xen_hvm_get_mem_type a = {
                .domid = DOMID_SELF,
                .pfn = pfn,
        };
-       int ram;
 
-       if (HYPERVISOR_hvm_op(HVMOP_get_mem_type, &a))
-               return -ENXIO;
-
-       switch (a.mem_type) {
-       case HVMMEM_mmio_dm:
-               ram = 0;
-               break;
-       case HVMMEM_ram_rw:
-       case HVMMEM_ram_ro:
-       default:
-               ram = 1;
-               break;
+       if (HYPERVISOR_hvm_op(HVMOP_get_mem_type, &a)) {
+               pr_warn_once("Unexpected HVMOP_get_mem_type failure\n");
+               return true;
        }
-
-       return ram;
+       return a.mem_type != HVMMEM_mmio_dm;
 }
+static struct vmcore_cb xen_vmcore_cb = {
+       .pfn_is_ram = xen_vmcore_pfn_is_ram,
+};
 #endif
 
 static void xen_hvm_exit_mmap(struct mm_struct *mm)
@@ -75,6 +64,6 @@ void __init xen_hvm_init_mmu_ops(void)
        if (is_pagetable_dying_supported())
                pv_ops.mmu.exit_mmap = xen_hvm_exit_mmap;
 #ifdef CONFIG_PROC_VMCORE
-       WARN_ON(register_oldmem_pfn_is_ram(&xen_oldmem_pfn_is_ram));
+       register_vmcore_cb(&xen_vmcore_cb);
 #endif
 }
index 571da0c..f3d79ed 100644 (file)
@@ -1668,13 +1668,10 @@ __dump_topology_ref_history(struct drm_dp_mst_topology_ref_history *history,
        for (i = 0; i < history->len; i++) {
                const struct drm_dp_mst_topology_ref_entry *entry =
                        &history->entries[i];
-               ulong *entries;
-               uint nr_entries;
                u64 ts_nsec = entry->ts_nsec;
                u32 rem_nsec = do_div(ts_nsec, 1000000000);
 
-               nr_entries = stack_depot_fetch(entry->backtrace, &entries);
-               stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 4);
+               stack_depot_snprint(entry->backtrace, buf, PAGE_SIZE, 4);
 
                drm_printf(&p, "  %d %ss (last at %5llu.%06u):\n%s",
                           entry->count,
index 93d48a6..7d1c578 100644 (file)
@@ -118,8 +118,6 @@ static noinline void save_stack(struct drm_mm_node *node)
 static void show_leaks(struct drm_mm *mm)
 {
        struct drm_mm_node *node;
-       unsigned long *entries;
-       unsigned int nr_entries;
        char *buf;
 
        buf = kmalloc(BUFSZ, GFP_KERNEL);
@@ -133,8 +131,7 @@ static void show_leaks(struct drm_mm *mm)
                        continue;
                }
 
-               nr_entries = stack_depot_fetch(node->stack, &entries);
-               stack_trace_snprint(buf, BUFSZ, entries, nr_entries, 0);
+               stack_depot_snprint(node->stack, buf, BUFSZ, 0);
                DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
                          node->start, node->size, buf);
        }
index 90546fa..bef795e 100644 (file)
@@ -56,8 +56,6 @@ void i915_vma_free(struct i915_vma *vma)
 
 static void vma_print_allocator(struct i915_vma *vma, const char *reason)
 {
-       unsigned long *entries;
-       unsigned int nr_entries;
        char buf[512];
 
        if (!vma->node.stack) {
@@ -66,8 +64,7 @@ static void vma_print_allocator(struct i915_vma *vma, const char *reason)
                return;
        }
 
-       nr_entries = stack_depot_fetch(vma->node.stack, &entries);
-       stack_trace_snprint(buf, sizeof(buf), entries, nr_entries, 0);
+       stack_depot_snprint(vma->node.stack, buf, sizeof(buf), 0);
        DRM_DEBUG_DRIVER("vma.node [%08llx + %08llx] %s: inserted at %s\n",
                         vma->node.start, vma->node.size, reason, buf);
 }
index eaf7688..0d85f3c 100644 (file)
@@ -65,16 +65,6 @@ static noinline depot_stack_handle_t __save_depot_stack(void)
        return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN);
 }
 
-static void __print_depot_stack(depot_stack_handle_t stack,
-                               char *buf, int sz, int indent)
-{
-       unsigned long *entries;
-       unsigned int nr_entries;
-
-       nr_entries = stack_depot_fetch(stack, &entries);
-       stack_trace_snprint(buf, sz, entries, nr_entries, indent);
-}
-
 static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
 {
        spin_lock_init(&rpm->debug.lock);
@@ -146,12 +136,12 @@ static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
                if (!buf)
                        return;
 
-               __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+               stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
                DRM_DEBUG_DRIVER("wakeref %x from\n%s", stack, buf);
 
                stack = READ_ONCE(rpm->debug.last_release);
                if (stack) {
-                       __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+                       stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
                        DRM_DEBUG_DRIVER("wakeref last released at\n%s", buf);
                }
 
@@ -183,12 +173,12 @@ __print_intel_runtime_pm_wakeref(struct drm_printer *p,
                return;
 
        if (dbg->last_acquire) {
-               __print_depot_stack(dbg->last_acquire, buf, PAGE_SIZE, 2);
+               stack_depot_snprint(dbg->last_acquire, buf, PAGE_SIZE, 2);
                drm_printf(p, "Wakeref last acquired:\n%s", buf);
        }
 
        if (dbg->last_release) {
-               __print_depot_stack(dbg->last_release, buf, PAGE_SIZE, 2);
+               stack_depot_snprint(dbg->last_release, buf, PAGE_SIZE, 2);
                drm_printf(p, "Wakeref last released:\n%s", buf);
        }
 
@@ -203,7 +193,7 @@ __print_intel_runtime_pm_wakeref(struct drm_printer *p,
                rep = 1;
                while (i + 1 < dbg->count && dbg->owners[i + 1] == stack)
                        rep++, i++;
-               __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+               stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
                drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
        }
 
index b05bce7..9dc15a5 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
+#include <linux/bits.h>
 #include <linux/string.h>
 
 int cxd2880_convert2s_complement(u32 value, u32 bitlen);
index 10607be..34f80b7 100644 (file)
@@ -111,6 +111,7 @@ config VIRTIO_MEM
        depends on MEMORY_HOTPLUG
        depends on MEMORY_HOTREMOVE
        depends on CONTIG_ALLOC
+       depends on EXCLUSIVE_SYSTEM_RAM
        help
         This driver provides access to virtio-mem paravirtualized memory
         devices, allowing to hotplug and hotunplug memory.
index bef8ad6..0da0af2 100644 (file)
@@ -223,6 +223,9 @@ struct virtio_mem {
         * When this lock is held the pointers can't change, ONLINE and
         * OFFLINE blocks can't change the state and no subblocks will get
         * plugged/unplugged.
+        *
+        * In kdump mode, used to serialize requests, last_block_addr and
+        * last_block_plugged.
         */
        struct mutex hotplug_mutex;
        bool hotplug_active;
@@ -230,6 +233,9 @@ struct virtio_mem {
        /* An error occurred we cannot handle - stop processing requests. */
        bool broken;
 
+       /* Cached valued of is_kdump_kernel() when the device was probed. */
+       bool in_kdump;
+
        /* The driver is being removed. */
        spinlock_t removal_lock;
        bool removing;
@@ -243,6 +249,13 @@ struct virtio_mem {
        /* Memory notifier (online/offline events). */
        struct notifier_block memory_notifier;
 
+#ifdef CONFIG_PROC_VMCORE
+       /* vmcore callback for /proc/vmcore handling in kdump mode */
+       struct vmcore_cb vmcore_cb;
+       uint64_t last_block_addr;
+       bool last_block_plugged;
+#endif /* CONFIG_PROC_VMCORE */
+
        /* Next device in the list of virtio-mem devices. */
        struct list_head next;
 };
@@ -260,6 +273,8 @@ static void virtio_mem_fake_offline_going_offline(unsigned long pfn,
 static void virtio_mem_fake_offline_cancel_offline(unsigned long pfn,
                                                   unsigned long nr_pages);
 static void virtio_mem_retry(struct virtio_mem *vm);
+static int virtio_mem_create_resource(struct virtio_mem *vm);
+static void virtio_mem_delete_resource(struct virtio_mem *vm);
 
 /*
  * Register a virtio-mem device so it will be considered for the online_page
@@ -2291,6 +2306,12 @@ static void virtio_mem_run_wq(struct work_struct *work)
        uint64_t diff;
        int rc;
 
+       if (unlikely(vm->in_kdump)) {
+               dev_warn_once(&vm->vdev->dev,
+                            "unexpected workqueue run in kdump kernel\n");
+               return;
+       }
+
        hrtimer_cancel(&vm->retry_timer);
 
        if (vm->broken)
@@ -2392,41 +2413,11 @@ static int virtio_mem_init_vq(struct virtio_mem *vm)
        return 0;
 }
 
-static int virtio_mem_init(struct virtio_mem *vm)
+static int virtio_mem_init_hotplug(struct virtio_mem *vm)
 {
        const struct range pluggable_range = mhp_get_pluggable_range(true);
-       uint64_t sb_size, addr;
-       uint16_t node_id;
-
-       if (!vm->vdev->config->get) {
-               dev_err(&vm->vdev->dev, "config access disabled\n");
-               return -EINVAL;
-       }
-
-       /*
-        * We don't want to (un)plug or reuse any memory when in kdump. The
-        * memory is still accessible (but not mapped).
-        */
-       if (is_kdump_kernel()) {
-               dev_warn(&vm->vdev->dev, "disabled in kdump kernel\n");
-               return -EBUSY;
-       }
-
-       /* Fetch all properties that can't change. */
-       virtio_cread_le(vm->vdev, struct virtio_mem_config, plugged_size,
-                       &vm->plugged_size);
-       virtio_cread_le(vm->vdev, struct virtio_mem_config, block_size,
-                       &vm->device_block_size);
-       virtio_cread_le(vm->vdev, struct virtio_mem_config, node_id,
-                       &node_id);
-       vm->nid = virtio_mem_translate_node_id(vm, node_id);
-       virtio_cread_le(vm->vdev, struct virtio_mem_config, addr, &vm->addr);
-       virtio_cread_le(vm->vdev, struct virtio_mem_config, region_size,
-                       &vm->region_size);
-
-       /* Determine the nid for the device based on the lowest address. */
-       if (vm->nid == NUMA_NO_NODE)
-               vm->nid = memory_add_physaddr_to_nid(vm->addr);
+       uint64_t unit_pages, sb_size, addr;
+       int rc;
 
        /* bad device setup - warn only */
        if (!IS_ALIGNED(vm->addr, memory_block_size_bytes()))
@@ -2496,10 +2487,6 @@ static int virtio_mem_init(struct virtio_mem *vm)
                                              vm->offline_threshold);
        }
 
-       dev_info(&vm->vdev->dev, "start address: 0x%llx", vm->addr);
-       dev_info(&vm->vdev->dev, "region size: 0x%llx", vm->region_size);
-       dev_info(&vm->vdev->dev, "device block size: 0x%llx",
-                (unsigned long long)vm->device_block_size);
        dev_info(&vm->vdev->dev, "memory block size: 0x%lx",
                 memory_block_size_bytes());
        if (vm->in_sbm)
@@ -2508,10 +2495,170 @@ static int virtio_mem_init(struct virtio_mem *vm)
        else
                dev_info(&vm->vdev->dev, "big block size: 0x%llx",
                         (unsigned long long)vm->bbm.bb_size);
+
+       /* create the parent resource for all memory */
+       rc = virtio_mem_create_resource(vm);
+       if (rc)
+               return rc;
+
+       /* use a single dynamic memory group to cover the whole memory device */
+       if (vm->in_sbm)
+               unit_pages = PHYS_PFN(memory_block_size_bytes());
+       else
+               unit_pages = PHYS_PFN(vm->bbm.bb_size);
+       rc = memory_group_register_dynamic(vm->nid, unit_pages);
+       if (rc < 0)
+               goto out_del_resource;
+       vm->mgid = rc;
+
+       /*
+        * If we still have memory plugged, we have to unplug all memory first.
+        * Registering our parent resource makes sure that this memory isn't
+        * actually in use (e.g., trying to reload the driver).
+        */
+       if (vm->plugged_size) {
+               vm->unplug_all_required = true;
+               dev_info(&vm->vdev->dev, "unplugging all memory is required\n");
+       }
+
+       /* register callbacks */
+       vm->memory_notifier.notifier_call = virtio_mem_memory_notifier_cb;
+       rc = register_memory_notifier(&vm->memory_notifier);
+       if (rc)
+               goto out_unreg_group;
+       rc = register_virtio_mem_device(vm);
+       if (rc)
+               goto out_unreg_mem;
+
+       return 0;
+out_unreg_mem:
+       unregister_memory_notifier(&vm->memory_notifier);
+out_unreg_group:
+       memory_group_unregister(vm->mgid);
+out_del_resource:
+       virtio_mem_delete_resource(vm);
+       return rc;
+}
+
+#ifdef CONFIG_PROC_VMCORE
+static int virtio_mem_send_state_request(struct virtio_mem *vm, uint64_t addr,
+                                        uint64_t size)
+{
+       const uint64_t nb_vm_blocks = size / vm->device_block_size;
+       const struct virtio_mem_req req = {
+               .type = cpu_to_virtio16(vm->vdev, VIRTIO_MEM_REQ_STATE),
+               .u.state.addr = cpu_to_virtio64(vm->vdev, addr),
+               .u.state.nb_blocks = cpu_to_virtio16(vm->vdev, nb_vm_blocks),
+       };
+       int rc = -ENOMEM;
+
+       dev_dbg(&vm->vdev->dev, "requesting state: 0x%llx - 0x%llx\n", addr,
+               addr + size - 1);
+
+       switch (virtio_mem_send_request(vm, &req)) {
+       case VIRTIO_MEM_RESP_ACK:
+               return virtio16_to_cpu(vm->vdev, vm->resp.u.state.state);
+       case VIRTIO_MEM_RESP_ERROR:
+               rc = -EINVAL;
+               break;
+       default:
+               break;
+       }
+
+       dev_dbg(&vm->vdev->dev, "requesting state failed: %d\n", rc);
+       return rc;
+}
+
+static bool virtio_mem_vmcore_pfn_is_ram(struct vmcore_cb *cb,
+                                        unsigned long pfn)
+{
+       struct virtio_mem *vm = container_of(cb, struct virtio_mem,
+                                            vmcore_cb);
+       uint64_t addr = PFN_PHYS(pfn);
+       bool is_ram;
+       int rc;
+
+       if (!virtio_mem_contains_range(vm, addr, PAGE_SIZE))
+               return true;
+       if (!vm->plugged_size)
+               return false;
+
+       /*
+        * We have to serialize device requests and access to the information
+        * about the block queried last.
+        */
+       mutex_lock(&vm->hotplug_mutex);
+
+       addr = ALIGN_DOWN(addr, vm->device_block_size);
+       if (addr != vm->last_block_addr) {
+               rc = virtio_mem_send_state_request(vm, addr,
+                                                  vm->device_block_size);
+               /* On any kind of error, we're going to signal !ram. */
+               if (rc == VIRTIO_MEM_STATE_PLUGGED)
+                       vm->last_block_plugged = true;
+               else
+                       vm->last_block_plugged = false;
+               vm->last_block_addr = addr;
+       }
+
+       is_ram = vm->last_block_plugged;
+       mutex_unlock(&vm->hotplug_mutex);
+       return is_ram;
+}
+#endif /* CONFIG_PROC_VMCORE */
+
+static int virtio_mem_init_kdump(struct virtio_mem *vm)
+{
+#ifdef CONFIG_PROC_VMCORE
+       dev_info(&vm->vdev->dev, "memory hot(un)plug disabled in kdump kernel\n");
+       vm->vmcore_cb.pfn_is_ram = virtio_mem_vmcore_pfn_is_ram;
+       register_vmcore_cb(&vm->vmcore_cb);
+       return 0;
+#else /* CONFIG_PROC_VMCORE */
+       dev_warn(&vm->vdev->dev, "disabled in kdump kernel without vmcore\n");
+       return -EBUSY;
+#endif /* CONFIG_PROC_VMCORE */
+}
+
+static int virtio_mem_init(struct virtio_mem *vm)
+{
+       uint16_t node_id;
+
+       if (!vm->vdev->config->get) {
+               dev_err(&vm->vdev->dev, "config access disabled\n");
+               return -EINVAL;
+       }
+
+       /* Fetch all properties that can't change. */
+       virtio_cread_le(vm->vdev, struct virtio_mem_config, plugged_size,
+                       &vm->plugged_size);
+       virtio_cread_le(vm->vdev, struct virtio_mem_config, block_size,
+                       &vm->device_block_size);
+       virtio_cread_le(vm->vdev, struct virtio_mem_config, node_id,
+                       &node_id);
+       vm->nid = virtio_mem_translate_node_id(vm, node_id);
+       virtio_cread_le(vm->vdev, struct virtio_mem_config, addr, &vm->addr);
+       virtio_cread_le(vm->vdev, struct virtio_mem_config, region_size,
+                       &vm->region_size);
+
+       /* Determine the nid for the device based on the lowest address. */
+       if (vm->nid == NUMA_NO_NODE)
+               vm->nid = memory_add_physaddr_to_nid(vm->addr);
+
+       dev_info(&vm->vdev->dev, "start address: 0x%llx", vm->addr);
+       dev_info(&vm->vdev->dev, "region size: 0x%llx", vm->region_size);
+       dev_info(&vm->vdev->dev, "device block size: 0x%llx",
+                (unsigned long long)vm->device_block_size);
        if (vm->nid != NUMA_NO_NODE && IS_ENABLED(CONFIG_NUMA))
                dev_info(&vm->vdev->dev, "nid: %d", vm->nid);
 
-       return 0;
+       /*
+        * We don't want to (un)plug or reuse any memory when in kdump. The
+        * memory is still accessible (but not exposed to Linux).
+        */
+       if (vm->in_kdump)
+               return virtio_mem_init_kdump(vm);
+       return virtio_mem_init_hotplug(vm);
 }
 
 static int virtio_mem_create_resource(struct virtio_mem *vm)
@@ -2525,8 +2672,10 @@ static int virtio_mem_create_resource(struct virtio_mem *vm)
        if (!name)
                return -ENOMEM;
 
+       /* Disallow mapping device memory via /dev/mem completely. */
        vm->parent_resource = __request_mem_region(vm->addr, vm->region_size,
-                                                  name, IORESOURCE_SYSTEM_RAM);
+                                                  name, IORESOURCE_SYSTEM_RAM |
+                                                  IORESOURCE_EXCLUSIVE);
        if (!vm->parent_resource) {
                kfree(name);
                dev_warn(&vm->vdev->dev, "could not reserve device region\n");
@@ -2571,7 +2720,6 @@ static bool virtio_mem_has_memory_added(struct virtio_mem *vm)
 static int virtio_mem_probe(struct virtio_device *vdev)
 {
        struct virtio_mem *vm;
-       uint64_t unit_pages;
        int rc;
 
        BUILD_BUG_ON(sizeof(struct virtio_mem_req) != 24);
@@ -2590,6 +2738,7 @@ static int virtio_mem_probe(struct virtio_device *vdev)
        hrtimer_init(&vm->retry_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        vm->retry_timer.function = virtio_mem_timer_expired;
        vm->retry_timer_ms = VIRTIO_MEM_RETRY_TIMER_MIN_MS;
+       vm->in_kdump = is_kdump_kernel();
 
        /* register the virtqueue */
        rc = virtio_mem_init_vq(vm);
@@ -2601,53 +2750,15 @@ static int virtio_mem_probe(struct virtio_device *vdev)
        if (rc)
                goto out_del_vq;
 
-       /* create the parent resource for all memory */
-       rc = virtio_mem_create_resource(vm);
-       if (rc)
-               goto out_del_vq;
-
-       /* use a single dynamic memory group to cover the whole memory device */
-       if (vm->in_sbm)
-               unit_pages = PHYS_PFN(memory_block_size_bytes());
-       else
-               unit_pages = PHYS_PFN(vm->bbm.bb_size);
-       rc = memory_group_register_dynamic(vm->nid, unit_pages);
-       if (rc < 0)
-               goto out_del_resource;
-       vm->mgid = rc;
-
-       /*
-        * If we still have memory plugged, we have to unplug all memory first.
-        * Registering our parent resource makes sure that this memory isn't
-        * actually in use (e.g., trying to reload the driver).
-        */
-       if (vm->plugged_size) {
-               vm->unplug_all_required = true;
-               dev_info(&vm->vdev->dev, "unplugging all memory is required\n");
-       }
-
-       /* register callbacks */
-       vm->memory_notifier.notifier_call = virtio_mem_memory_notifier_cb;
-       rc = register_memory_notifier(&vm->memory_notifier);
-       if (rc)
-               goto out_unreg_group;
-       rc = register_virtio_mem_device(vm);
-       if (rc)
-               goto out_unreg_mem;
-
        virtio_device_ready(vdev);
 
        /* trigger a config update to start processing the requested_size */
-       atomic_set(&vm->config_changed, 1);
-       queue_work(system_freezable_wq, &vm->wq);
+       if (!vm->in_kdump) {
+               atomic_set(&vm->config_changed, 1);
+               queue_work(system_freezable_wq, &vm->wq);
+       }
 
        return 0;
-out_unreg_mem:
-       unregister_memory_notifier(&vm->memory_notifier);
-out_unreg_group:
-       memory_group_unregister(vm->mgid);
-out_del_resource:
-       virtio_mem_delete_resource(vm);
 out_del_vq:
        vdev->config->del_vqs(vdev);
 out_free_vm:
@@ -2657,9 +2768,8 @@ out_free_vm:
        return rc;
 }
 
-static void virtio_mem_remove(struct virtio_device *vdev)
+static void virtio_mem_deinit_hotplug(struct virtio_mem *vm)
 {
-       struct virtio_mem *vm = vdev->priv;
        unsigned long mb_id;
        int rc;
 
@@ -2706,7 +2816,8 @@ static void virtio_mem_remove(struct virtio_device *vdev)
         * away. Warn at least.
         */
        if (virtio_mem_has_memory_added(vm)) {
-               dev_warn(&vdev->dev, "device still has system memory added\n");
+               dev_warn(&vm->vdev->dev,
+                        "device still has system memory added\n");
        } else {
                virtio_mem_delete_resource(vm);
                kfree_const(vm->resource_name);
@@ -2720,6 +2831,23 @@ static void virtio_mem_remove(struct virtio_device *vdev)
        } else {
                vfree(vm->bbm.bb_states);
        }
+}
+
+static void virtio_mem_deinit_kdump(struct virtio_mem *vm)
+{
+#ifdef CONFIG_PROC_VMCORE
+       unregister_vmcore_cb(&vm->vmcore_cb);
+#endif /* CONFIG_PROC_VMCORE */
+}
+
+static void virtio_mem_remove(struct virtio_device *vdev)
+{
+       struct virtio_mem *vm = vdev->priv;
+
+       if (vm->in_kdump)
+               virtio_mem_deinit_kdump(vm);
+       else
+               virtio_mem_deinit_hotplug(vm);
 
        /* reset the device and cleanup the queues */
        vdev->config->reset(vdev);
@@ -2733,6 +2861,9 @@ static void virtio_mem_config_changed(struct virtio_device *vdev)
 {
        struct virtio_mem *vm = vdev->priv;
 
+       if (unlikely(vm->in_kdump))
+               return;
+
        atomic_set(&vm->config_changed, 1);
        virtio_mem_retry(vm);
 }
index fa58274..f8c7f26 100644 (file)
@@ -156,7 +156,7 @@ static int padzero(unsigned long elf_bss)
 #define STACK_ADD(sp, items) ((elf_addr_t __user *)(sp) - (items))
 #define STACK_ROUND(sp, items) \
        (((unsigned long) (sp - items)) &~ 15UL)
-#define STACK_ALLOC(sp, len) ({ sp -= len ; sp; })
+#define STACK_ALLOC(sp, len) (sp -= len)
 #endif
 
 #ifndef ELF_BASE_PLATFORM
@@ -1074,20 +1074,26 @@ out_free_interp:
 
                vaddr = elf_ppnt->p_vaddr;
                /*
-                * If we are loading ET_EXEC or we have already performed
-                * the ET_DYN load_addr calculations, proceed normally.
+                * The first time through the loop, load_addr_set is false:
+                * layout will be calculated. Once set, use MAP_FIXED since
+                * we know we've already safely mapped the entire region with
+                * MAP_FIXED_NOREPLACE in the once-per-binary logic following.
                 */
-               if (elf_ex->e_type == ET_EXEC || load_addr_set) {
+               if (load_addr_set) {
                        elf_flags |= MAP_FIXED;
+               } else if (elf_ex->e_type == ET_EXEC) {
+                       /*
+                        * This logic is run once for the first LOAD Program
+                        * Header for ET_EXEC binaries. No special handling
+                        * is needed.
+                        */
+                       elf_flags |= MAP_FIXED_NOREPLACE;
                } else if (elf_ex->e_type == ET_DYN) {
                        /*
                         * This logic is run once for the first LOAD Program
                         * Header for ET_DYN binaries to calculate the
                         * randomization (load_bias) for all the LOAD
-                        * Program Headers, and to calculate the entire
-                        * size of the ELF mapping (total_size). (Note that
-                        * load_addr_set is set to true later once the
-                        * initial mapping is performed.)
+                        * Program Headers.
                         *
                         * There are effectively two types of ET_DYN
                         * binaries: programs (i.e. PIE: ET_DYN with INTERP)
@@ -1108,7 +1114,7 @@ out_free_interp:
                         * Therefore, programs are loaded offset from
                         * ELF_ET_DYN_BASE and loaders are loaded into the
                         * independently randomized mmap region (0 load_bias
-                        * without MAP_FIXED).
+                        * without MAP_FIXED nor MAP_FIXED_NOREPLACE).
                         */
                        if (interpreter) {
                                load_bias = ELF_ET_DYN_BASE;
@@ -1117,7 +1123,7 @@ out_free_interp:
                                alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
                                if (alignment)
                                        load_bias &= ~(alignment - 1);
-                               elf_flags |= MAP_FIXED;
+                               elf_flags |= MAP_FIXED_NOREPLACE;
                        } else
                                load_bias = 0;
 
@@ -1129,7 +1135,14 @@ out_free_interp:
                         * is then page aligned.
                         */
                        load_bias = ELF_PAGESTART(load_bias - vaddr);
+               }
 
+               /*
+                * Calculate the entire size of the ELF mapping (total_size).
+                * (Note that load_addr_set is set to true later once the
+                * initial mapping is performed.)
+                */
+               if (!load_addr_set) {
                        total_size = total_mapping_size(elf_phdata,
                                                        elf_ex->e_phnum);
                        if (!total_size) {
index 06855f6..62a3d25 100644 (file)
@@ -63,9 +63,10 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
        struct inode *inode;
        struct coda_inode_info *cii;
        unsigned long hash = coda_f2i(fid);
+       umode_t inode_type = coda_inode_type(attr);
 
+retry:
        inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid);
-
        if (!inode)
                return ERR_PTR(-ENOMEM);
 
@@ -75,11 +76,15 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
                inode->i_ino = hash;
                /* inode is locked and unique, no need to grab cii->c_lock */
                cii->c_mapcount = 0;
+               coda_fill_inode(inode, attr);
                unlock_new_inode(inode);
+       } else if ((inode->i_mode & S_IFMT) != inode_type) {
+               /* Inode has changed type, mark bad and grab a new one */
+               remove_inode_hash(inode);
+               coda_flag_inode(inode, C_PURGE);
+               iput(inode);
+               goto retry;
        }
-
-       /* always replace the attributes, type might have changed */
-       coda_fill_inode(inode, attr);
        return inode;
 }
 
index 2e1a5a1..903ca8f 100644 (file)
@@ -87,28 +87,27 @@ static struct coda_timespec timespec64_to_coda(struct timespec64 ts64)
 }
 
 /* utility functions below */
+umode_t coda_inode_type(struct coda_vattr *attr)
+{
+       switch (attr->va_type) {
+       case C_VREG:
+               return S_IFREG;
+       case C_VDIR:
+               return S_IFDIR;
+       case C_VLNK:
+               return S_IFLNK;
+       case C_VNON:
+       default:
+               return 0;
+       }
+}
+
 void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
 {
-        int inode_type;
-        /* inode's i_flags, i_ino are set by iget 
-           XXX: is this all we need ??
-           */
-        switch (attr->va_type) {
-        case C_VNON:
-                inode_type  = 0;
-                break;
-        case C_VREG:
-                inode_type = S_IFREG;
-                break;
-        case C_VDIR:
-                inode_type = S_IFDIR;
-                break;
-        case C_VLNK:
-                inode_type = S_IFLNK;
-                break;
-        default:
-                inode_type = 0;
-        }
+       /* inode's i_flags, i_ino are set by iget
+        * XXX: is this all we need ??
+        */
+       umode_t inode_type = coda_inode_type(attr);
        inode->i_mode |= inode_type;
 
        if (attr->va_mode != (u_short) -1)
index e7b2775..9be281b 100644 (file)
@@ -53,10 +53,11 @@ int coda_getattr(struct user_namespace *, const struct path *, struct kstat *,
                 u32, unsigned int);
 int coda_setattr(struct user_namespace *, struct dentry *, struct iattr *);
 
-/* this file:  heloers */
+/* this file:  helpers */
 char *coda_f2s(struct CodaFid *f);
 int coda_iscontrol(const char *name, size_t length);
 
+umode_t coda_inode_type(struct coda_vattr *attr);
 void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
 void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
 unsigned short coda_flags_to_cflags(unsigned short);
@@ -83,6 +84,9 @@ static __inline__ void coda_flag_inode(struct inode *inode, int flag)
 {
        struct coda_inode_info *cii = ITOC(inode);
 
+       if (!inode)
+               return;
+
        spin_lock(&cii->c_lock);
        cii->c_flags |= flag;
        spin_unlock(&cii->c_lock);
index d69989c..328d7a6 100644 (file)
@@ -317,13 +317,10 @@ static int coda_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
                                coda_dir_drop_nlink(old_dir);
                                coda_dir_inc_nlink(new_dir);
                        }
-                       coda_dir_update_mtime(old_dir);
-                       coda_dir_update_mtime(new_dir);
                        coda_flag_inode(d_inode(new_dentry), C_VATTR);
-               } else {
-                       coda_flag_inode(old_dir, C_VATTR);
-                       coda_flag_inode(new_dir, C_VATTR);
                }
+               coda_dir_update_mtime(old_dir);
+               coda_dir_update_mtime(new_dir);
        }
        return error;
 }
@@ -499,15 +496,20 @@ out:
  */
 static int coda_dentry_delete(const struct dentry * dentry)
 {
-       int flags;
+       struct inode *inode;
+       struct coda_inode_info *cii;
 
        if (d_really_is_negative(dentry)) 
                return 0;
 
-       flags = (ITOC(d_inode(dentry))->c_flags) & C_PURGE;
-       if (is_bad_inode(d_inode(dentry)) || flags) {
+       inode = d_inode(dentry);
+       if (!inode || is_bad_inode(inode))
                return 1;
-       }
+
+       cii = ITOC(inode);
+       if (cii->c_flags & C_PURGE)
+               return 1;
+
        return 0;
 }
 
index ef5ca22..29dd87b 100644 (file)
@@ -8,6 +8,7 @@
  * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
  */
 
+#include <linux/refcount.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/time.h>
@@ -28,7 +29,7 @@
 #include "coda_int.h"
 
 struct coda_vm_ops {
-       atomic_t refcnt;
+       refcount_t refcnt;
        struct file *coda_file;
        const struct vm_operations_struct *host_vm_ops;
        struct vm_operations_struct vm_ops;
@@ -98,7 +99,7 @@ coda_vm_open(struct vm_area_struct *vma)
        struct coda_vm_ops *cvm_ops =
                container_of(vma->vm_ops, struct coda_vm_ops, vm_ops);
 
-       atomic_inc(&cvm_ops->refcnt);
+       refcount_inc(&cvm_ops->refcnt);
 
        if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->open)
                cvm_ops->host_vm_ops->open(vma);
@@ -113,7 +114,7 @@ coda_vm_close(struct vm_area_struct *vma)
        if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->close)
                cvm_ops->host_vm_ops->close(vma);
 
-       if (atomic_dec_and_test(&cvm_ops->refcnt)) {
+       if (refcount_dec_and_test(&cvm_ops->refcnt)) {
                vma->vm_ops = cvm_ops->host_vm_ops;
                fput(cvm_ops->coda_file);
                kfree(cvm_ops);
@@ -189,7 +190,7 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
                cvm_ops->vm_ops.open = coda_vm_open;
                cvm_ops->vm_ops.close = coda_vm_close;
                cvm_ops->coda_file = coda_file;
-               atomic_set(&cvm_ops->refcnt, 1);
+               refcount_set(&cvm_ops->refcnt, 1);
 
                vma->vm_ops = &cvm_ops->vm_ops;
        }
@@ -238,11 +239,10 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)
        struct coda_file_info *cfi;
        struct coda_inode_info *cii;
        struct inode *host_inode;
-       int err;
 
        cfi = coda_ftoc(coda_file);
 
-       err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
+       venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
                          coda_flags, coda_file->f_cred->fsuid);
 
        host_inode = file_inode(cfi->cfi_container);
index 240669f..b39580a 100644 (file)
@@ -122,14 +122,10 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf,
                                hdr.opcode, hdr.unique);
                        nbytes = size;
                }
-               dcbuf = kvmalloc(nbytes, GFP_KERNEL);
-               if (!dcbuf) {
-                       retval = -ENOMEM;
-                       goto out;
-               }
-               if (copy_from_user(dcbuf, buf, nbytes)) {
-                       kvfree(dcbuf);
-                       retval = -EFAULT;
+
+               dcbuf = vmemdup_user(buf, nbytes);
+               if (IS_ERR(dcbuf)) {
+                       retval = PTR_ERR(dcbuf);
                        goto out;
                }
 
@@ -388,7 +384,7 @@ MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
 MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
 MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
 MODULE_LICENSE("GPL");
-MODULE_VERSION("7.0");
+MODULE_VERSION("7.2");
 
 static int __init init_coda(void)
 {
index eb3b189..59f6cfd 100644 (file)
@@ -744,7 +744,8 @@ static int coda_upcall(struct venus_comm *vcp,
        list_add_tail(&req->uc_chain, &vcp->vc_pending);
        wake_up_interruptible(&vcp->vc_waitq);
 
-       if (req->uc_flags & CODA_REQ_ASYNC) {
+       /* We can return early on asynchronous requests */
+       if (outSize == NULL) {
                mutex_unlock(&vcp->vc_mutex);
                return 0;
        }
index 4a95a92..2a51432 100644 (file)
@@ -462,8 +462,7 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
                goto out;
 
        if (S_ISDIR(main_inode->i_mode)) {
-               if (fd.entrylength < sizeof(struct hfs_cat_dir))
-                       /* panic? */;
+               WARN_ON(fd.entrylength < sizeof(struct hfs_cat_dir));
                hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
                           sizeof(struct hfs_cat_dir));
                if (rec.type != HFS_CDR_DIR ||
@@ -483,8 +482,7 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
                hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
                                sizeof(struct hfs_cat_file));
        } else {
-               if (fd.entrylength < sizeof(struct hfs_cat_file))
-                       /* panic? */;
+               WARN_ON(fd.entrylength < sizeof(struct hfs_cat_file));
                hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
                           sizeof(struct hfs_cat_file));
                if (rec.type != HFS_CDR_FIL ||
index 6fef67c..d08a8d1 100644 (file)
@@ -509,8 +509,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
        if (type == HFSPLUS_FOLDER) {
                struct hfsplus_cat_folder *folder = &entry.folder;
 
-               if (fd->entrylength < sizeof(struct hfsplus_cat_folder))
-                       /* panic? */;
+               WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_folder));
                hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
                                        sizeof(struct hfsplus_cat_folder));
                hfsplus_get_perms(inode, &folder->permissions, 1);
@@ -530,8 +529,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
        } else if (type == HFSPLUS_FILE) {
                struct hfsplus_cat_file *file = &entry.file;
 
-               if (fd->entrylength < sizeof(struct hfsplus_cat_file))
-                       /* panic? */;
+               WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_file));
                hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
                                        sizeof(struct hfsplus_cat_file));
 
@@ -588,8 +586,7 @@ int hfsplus_cat_write_inode(struct inode *inode)
        if (S_ISDIR(main_inode->i_mode)) {
                struct hfsplus_cat_folder *folder = &entry.folder;
 
-               if (fd.entrylength < sizeof(struct hfsplus_cat_folder))
-                       /* panic? */;
+               WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_folder));
                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
                                        sizeof(struct hfsplus_cat_folder));
                /* simple node checks? */
@@ -614,8 +611,7 @@ int hfsplus_cat_write_inode(struct inode *inode)
        } else {
                struct hfsplus_cat_file *file = &entry.file;
 
-               if (fd.entrylength < sizeof(struct hfsplus_cat_file))
-                       /* panic? */;
+               WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_file));
                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
                                        sizeof(struct hfsplus_cat_file));
                hfsplus_inode_write_fork(inode, &file->data_fork);
index cdfb1ae..49d2e68 100644 (file)
@@ -1446,8 +1446,8 @@ static int get_hstate_idx(int page_size_log)
  * otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
  */
 struct file *hugetlb_file_setup(const char *name, size_t size,
-                               vm_flags_t acctflag, struct ucounts **ucounts,
-                               int creat_flags, int page_size_log)
+                               vm_flags_t acctflag, int creat_flags,
+                               int page_size_log)
 {
        struct inode *inode;
        struct vfsmount *mnt;
@@ -1458,22 +1458,19 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
        if (hstate_idx < 0)
                return ERR_PTR(-ENODEV);
 
-       *ucounts = NULL;
        mnt = hugetlbfs_vfsmount[hstate_idx];
        if (!mnt)
                return ERR_PTR(-ENOENT);
 
        if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
-               *ucounts = current_ucounts();
-               if (user_shm_lock(size, *ucounts)) {
-                       task_lock(current);
-                       pr_warn_once("%s (%d): Using mlock ulimits for SHM_HUGETLB is deprecated\n",
+               struct ucounts *ucounts = current_ucounts();
+
+               if (user_shm_lock(size, ucounts)) {
+                       pr_warn_once("%s (%d): Using mlock ulimits for SHM_HUGETLB is obsolete\n",
                                current->comm, current->pid);
-                       task_unlock(current);
-               } else {
-                       *ucounts = NULL;
-                       return ERR_PTR(-EPERM);
+                       user_shm_unlock(size, ucounts);
                }
+               return ERR_PTR(-EPERM);
        }
 
        file = ERR_PTR(-ENOSPC);
@@ -1498,10 +1495,6 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
 
        iput(inode);
 out:
-       if (*ucounts) {
-               user_shm_unlock(size, *ucounts);
-               *ucounts = NULL;
-       }
        return file;
 }
 
index 9abc88d..3eba094 100644 (file)
@@ -428,11 +428,20 @@ void ihold(struct inode *inode)
 }
 EXPORT_SYMBOL(ihold);
 
-static void inode_lru_list_add(struct inode *inode)
+static void __inode_add_lru(struct inode *inode, bool rotate)
 {
+       if (inode->i_state & (I_DIRTY_ALL | I_SYNC | I_FREEING | I_WILL_FREE))
+               return;
+       if (atomic_read(&inode->i_count))
+               return;
+       if (!(inode->i_sb->s_flags & SB_ACTIVE))
+               return;
+       if (!mapping_shrinkable(&inode->i_data))
+               return;
+
        if (list_lru_add(&inode->i_sb->s_inode_lru, &inode->i_lru))
                this_cpu_inc(nr_unused);
-       else
+       else if (rotate)
                inode->i_state |= I_REFERENCED;
 }
 
@@ -443,16 +452,11 @@ static void inode_lru_list_add(struct inode *inode)
  */
 void inode_add_lru(struct inode *inode)
 {
-       if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC |
-                               I_FREEING | I_WILL_FREE)) &&
-           !atomic_read(&inode->i_count) && inode->i_sb->s_flags & SB_ACTIVE)
-               inode_lru_list_add(inode);
+       __inode_add_lru(inode, false);
 }
 
-
 static void inode_lru_list_del(struct inode *inode)
 {
-
        if (list_lru_del(&inode->i_sb->s_inode_lru, &inode->i_lru))
                this_cpu_dec(nr_unused);
 }
@@ -728,10 +732,6 @@ again:
 /*
  * Isolate the inode from the LRU in preparation for freeing it.
  *
- * Any inodes which are pinned purely because of attached pagecache have their
- * pagecache removed.  If the inode has metadata buffers attached to
- * mapping->private_list then try to remove them.
- *
  * If the inode has the I_REFERENCED flag set, then it means that it has been
  * used recently - the flag is set in iput_final(). When we encounter such an
  * inode, clear the flag and move it to the back of the LRU so it gets another
@@ -747,31 +747,39 @@ static enum lru_status inode_lru_isolate(struct list_head *item,
        struct inode    *inode = container_of(item, struct inode, i_lru);
 
        /*
-        * we are inverting the lru lock/inode->i_lock here, so use a trylock.
-        * If we fail to get the lock, just skip it.
+        * We are inverting the lru lock/inode->i_lock here, so use a
+        * trylock. If we fail to get the lock, just skip it.
         */
        if (!spin_trylock(&inode->i_lock))
                return LRU_SKIP;
 
        /*
-        * Referenced or dirty inodes are still in use. Give them another pass
-        * through the LRU as we canot reclaim them now.
+        * Inodes can get referenced, redirtied, or repopulated while
+        * they're already on the LRU, and this can make them
+        * unreclaimable for a while. Remove them lazily here; iput,
+        * sync, or the last page cache deletion will requeue them.
         */
        if (atomic_read(&inode->i_count) ||
-           (inode->i_state & ~I_REFERENCED)) {
+           (inode->i_state & ~I_REFERENCED) ||
+           !mapping_shrinkable(&inode->i_data)) {
                list_lru_isolate(lru, &inode->i_lru);
                spin_unlock(&inode->i_lock);
                this_cpu_dec(nr_unused);
                return LRU_REMOVED;
        }
 
-       /* recently referenced inodes get one more pass */
+       /* Recently referenced inodes get one more pass */
        if (inode->i_state & I_REFERENCED) {
                inode->i_state &= ~I_REFERENCED;
                spin_unlock(&inode->i_lock);
                return LRU_ROTATE;
        }
 
+       /*
+        * On highmem systems, mapping_shrinkable() permits dropping
+        * page cache in order to free up struct inodes: lowmem might
+        * be under pressure before the cache inside the highmem zone.
+        */
        if (inode_has_buffers(inode) || !mapping_empty(&inode->i_data)) {
                __iget(inode);
                spin_unlock(&inode->i_lock);
@@ -1638,7 +1646,7 @@ static void iput_final(struct inode *inode)
        if (!drop &&
            !(inode->i_state & I_DONTCACHE) &&
            (sb->s_flags & SB_ACTIVE)) {
-               inode_add_lru(inode);
+               __inode_add_lru(inode, true);
                spin_unlock(&inode->i_lock);
                return;
        }
index cdd83d4..7979ff8 100644 (file)
@@ -138,7 +138,6 @@ extern int vfs_open(const struct path *, struct file *);
  * inode.c
  */
 extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
-extern void inode_add_lru(struct inode *inode);
 extern int dentry_needs_remove_privs(struct dentry *dentry);
 
 /*
index adf3bb0..6ce8617 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * alloc.c - NILFS dat/inode allocator
+ * NILFS dat/inode allocator
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 0303c39..b667e86 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * alloc.h - persistent object (dat entry/disk inode) allocator/deallocator
+ * Persistent object (dat entry/disk inode) allocator/deallocator
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 5900879..798a2c1 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * bmap.c - NILFS block mapping.
+ * NILFS block mapping.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 2c63858..608168a 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * bmap.h - NILFS block mapping.
+ * NILFS block mapping.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 4391fd3..66bdaa2 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * btnode.c - NILFS B-tree node cache
+ * NILFS B-tree node cache
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 0f88dbc..1166365 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * btnode.h - NILFS B-tree node cache
+ * NILFS B-tree node cache
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index ab9ec07..3594eab 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * btree.c - NILFS B-tree.
+ * NILFS B-tree.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index d1421b6..92868e1 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * btree.h - NILFS B-tree.
+ * NILFS B-tree.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index ce14477..9ebefb3 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * cpfile.c - NILFS checkpoint file.
+ * NILFS checkpoint file.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 6336222..edabb2d 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * cpfile.h - NILFS checkpoint file.
+ * NILFS checkpoint file.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 8bccdf1..dc51d3b 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * dat.c - NILFS disk address translation.
+ * NILFS disk address translation.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index b17ee34..468c82d 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * dat.h - NILFS disk address translation.
+ * NILFS disk address translation.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 81394e2..f8f4c2f 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * dir.c - NILFS directory entry operations
+ * NILFS directory entry operations
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index f353101..a35f279 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * direct.c - NILFS direct block pointer.
+ * NILFS direct block pointer.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index ec9a23c..b7ca896 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * direct.h - NILFS direct block pointer.
+ * NILFS direct block pointer.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 7cf7652..a265d39 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * file.c - NILFS regular file handling primitives including fsync().
+ * NILFS regular file handling primitives including fsync().
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 4483204..a8f5315 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * gcinode.c - dummy inodes to buffer blocks for garbage collection
+ * Dummy inodes to buffer blocks for garbage collection
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 02727ed..a8a4bc8 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * ifile.c - NILFS inode file
+ * NILFS inode file
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index a1e1e57..35c5273 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * ifile.h - NILFS inode file
+ * NILFS inode file
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 2e8eb26..e3d807d 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * inode.c - NILFS inode operations.
+ * NILFS inode operations.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 1d0583c..fec194a 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * ioctl.c - NILFS ioctl operations.
+ * NILFS ioctl operations.
  *
  * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation.
  *
index 97769fe..4b3d33c 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * mdt.c - meta data file for NILFS
+ * Meta data file for NILFS
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index e77aea4..8f86080 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * mdt.h - NILFS meta data file prototype and definitions
+ * NILFS meta data file prototype and definitions
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 91eebeb..23899e0 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * namei.c - NILFS pathname lookup operations.
+ * NILFS pathname lookup operations.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 60b21b6..a7b8175 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * nilfs.h - NILFS local header file.
+ * NILFS local header file.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 171fb5c..bc3e2cd 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * page.c - buffer/page management specific to NILFS
+ * Buffer/page management specific to NILFS
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 62b9bb4..569263b 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * page.h - buffer/page management specific to NILFS
+ * Buffer/page management specific to NILFS
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 2217f90..9e2ed76 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * recovery.c - NILFS recovery logic
+ * NILFS recovery logic
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 56872e9..43287b0 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * segbuf.c - NILFS segment buffer
+ * NILFS segment buffer
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 9bea1bd..e20091e 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * segbuf.h - NILFS Segment buffer prototypes and definitions
+ * NILFS Segment buffer prototypes and definitions
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 686c8ee..85a8533 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * segment.c - NILFS segment constructor.
+ * NILFS segment constructor.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index f5cf530..1060f72 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * segment.h - NILFS Segment constructor prototypes and definitions
+ * NILFS Segment constructor prototypes and definitions
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 6372247..e385cca 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * sufile.c - NILFS segment usage file.
+ * NILFS segment usage file.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index c4e2c7a..8e8a1a5 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * sufile.h - NILFS segment usage file.
+ * NILFS segment usage file.
  *
  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
  *
index 3134c0e..63e5fa7 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * super.c - NILFS module and super block management.
+ * NILFS module and super block management.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 62f8a7a..81f35c5 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * sysfs.c - sysfs support implementation.
+ * Sysfs support implementation.
  *
  * Copyright (C) 2005-2014 Nippon Telegraph and Telephone Corporation.
  * Copyright (C) 2014 HGST, Inc., a Western Digital Company.
@@ -95,7 +95,7 @@ static ssize_t
 nilfs_snapshot_inodes_count_show(struct nilfs_snapshot_attr *attr,
                                 struct nilfs_root *root, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%llu\n",
+       return sysfs_emit(buf, "%llu\n",
                        (unsigned long long)atomic64_read(&root->inodes_count));
 }
 
@@ -103,7 +103,7 @@ static ssize_t
 nilfs_snapshot_blocks_count_show(struct nilfs_snapshot_attr *attr,
                                 struct nilfs_root *root, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%llu\n",
+       return sysfs_emit(buf, "%llu\n",
                        (unsigned long long)atomic64_read(&root->blocks_count));
 }
 
@@ -116,7 +116,7 @@ static ssize_t
 nilfs_snapshot_README_show(struct nilfs_snapshot_attr *attr,
                            struct nilfs_root *root, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, snapshot_readme_str);
+       return sysfs_emit(buf, snapshot_readme_str);
 }
 
 NILFS_SNAPSHOT_RO_ATTR(inodes_count);
@@ -217,7 +217,7 @@ static ssize_t
 nilfs_mounted_snapshots_README_show(struct nilfs_mounted_snapshots_attr *attr,
                                    struct the_nilfs *nilfs, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, mounted_snapshots_readme_str);
+       return sysfs_emit(buf, mounted_snapshots_readme_str);
 }
 
 NILFS_MOUNTED_SNAPSHOTS_RO_ATTR(README);
@@ -255,7 +255,7 @@ nilfs_checkpoints_checkpoints_number_show(struct nilfs_checkpoints_attr *attr,
 
        ncheckpoints = cpstat.cs_ncps;
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", ncheckpoints);
+       return sysfs_emit(buf, "%llu\n", ncheckpoints);
 }
 
 static ssize_t
@@ -278,7 +278,7 @@ nilfs_checkpoints_snapshots_number_show(struct nilfs_checkpoints_attr *attr,
 
        nsnapshots = cpstat.cs_nsss;
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", nsnapshots);
+       return sysfs_emit(buf, "%llu\n", nsnapshots);
 }
 
 static ssize_t
@@ -292,7 +292,7 @@ nilfs_checkpoints_last_seg_checkpoint_show(struct nilfs_checkpoints_attr *attr,
        last_cno = nilfs->ns_last_cno;
        spin_unlock(&nilfs->ns_last_segment_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", last_cno);
+       return sysfs_emit(buf, "%llu\n", last_cno);
 }
 
 static ssize_t
@@ -306,7 +306,7 @@ nilfs_checkpoints_next_checkpoint_show(struct nilfs_checkpoints_attr *attr,
        cno = nilfs->ns_cno;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", cno);
+       return sysfs_emit(buf, "%llu\n", cno);
 }
 
 static const char checkpoints_readme_str[] =
@@ -322,7 +322,7 @@ static ssize_t
 nilfs_checkpoints_README_show(struct nilfs_checkpoints_attr *attr,
                                struct the_nilfs *nilfs, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, checkpoints_readme_str);
+       return sysfs_emit(buf, checkpoints_readme_str);
 }
 
 NILFS_CHECKPOINTS_RO_ATTR(checkpoints_number);
@@ -353,7 +353,7 @@ nilfs_segments_segments_number_show(struct nilfs_segments_attr *attr,
                                     struct the_nilfs *nilfs,
                                     char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%lu\n", nilfs->ns_nsegments);
+       return sysfs_emit(buf, "%lu\n", nilfs->ns_nsegments);
 }
 
 static ssize_t
@@ -361,7 +361,7 @@ nilfs_segments_blocks_per_segment_show(struct nilfs_segments_attr *attr,
                                        struct the_nilfs *nilfs,
                                        char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%lu\n", nilfs->ns_blocks_per_segment);
+       return sysfs_emit(buf, "%lu\n", nilfs->ns_blocks_per_segment);
 }
 
 static ssize_t
@@ -375,7 +375,7 @@ nilfs_segments_clean_segments_show(struct nilfs_segments_attr *attr,
        ncleansegs = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile);
        up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%lu\n", ncleansegs);
+       return sysfs_emit(buf, "%lu\n", ncleansegs);
 }
 
 static ssize_t
@@ -395,7 +395,7 @@ nilfs_segments_dirty_segments_show(struct nilfs_segments_attr *attr,
                return err;
        }
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", sustat.ss_ndirtysegs);
+       return sysfs_emit(buf, "%llu\n", sustat.ss_ndirtysegs);
 }
 
 static const char segments_readme_str[] =
@@ -411,7 +411,7 @@ nilfs_segments_README_show(struct nilfs_segments_attr *attr,
                            struct the_nilfs *nilfs,
                            char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, segments_readme_str);
+       return sysfs_emit(buf, segments_readme_str);
 }
 
 NILFS_SEGMENTS_RO_ATTR(segments_number);
@@ -448,7 +448,7 @@ nilfs_segctor_last_pseg_block_show(struct nilfs_segctor_attr *attr,
        last_pseg = nilfs->ns_last_pseg;
        spin_unlock(&nilfs->ns_last_segment_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n",
+       return sysfs_emit(buf, "%llu\n",
                        (unsigned long long)last_pseg);
 }
 
@@ -463,7 +463,7 @@ nilfs_segctor_last_seg_sequence_show(struct nilfs_segctor_attr *attr,
        last_seq = nilfs->ns_last_seq;
        spin_unlock(&nilfs->ns_last_segment_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", last_seq);
+       return sysfs_emit(buf, "%llu\n", last_seq);
 }
 
 static ssize_t
@@ -477,7 +477,7 @@ nilfs_segctor_last_seg_checkpoint_show(struct nilfs_segctor_attr *attr,
        last_cno = nilfs->ns_last_cno;
        spin_unlock(&nilfs->ns_last_segment_lock);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", last_cno);
+       return sysfs_emit(buf, "%llu\n", last_cno);
 }
 
 static ssize_t
@@ -491,7 +491,7 @@ nilfs_segctor_current_seg_sequence_show(struct nilfs_segctor_attr *attr,
        seg_seq = nilfs->ns_seg_seq;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", seg_seq);
+       return sysfs_emit(buf, "%llu\n", seg_seq);
 }
 
 static ssize_t
@@ -505,7 +505,7 @@ nilfs_segctor_current_last_full_seg_show(struct nilfs_segctor_attr *attr,
        segnum = nilfs->ns_segnum;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", segnum);
+       return sysfs_emit(buf, "%llu\n", segnum);
 }
 
 static ssize_t
@@ -519,7 +519,7 @@ nilfs_segctor_next_full_seg_show(struct nilfs_segctor_attr *attr,
        nextnum = nilfs->ns_nextnum;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", nextnum);
+       return sysfs_emit(buf, "%llu\n", nextnum);
 }
 
 static ssize_t
@@ -533,7 +533,7 @@ nilfs_segctor_next_pseg_offset_show(struct nilfs_segctor_attr *attr,
        pseg_offset = nilfs->ns_pseg_offset;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%lu\n", pseg_offset);
+       return sysfs_emit(buf, "%lu\n", pseg_offset);
 }
 
 static ssize_t
@@ -547,7 +547,7 @@ nilfs_segctor_next_checkpoint_show(struct nilfs_segctor_attr *attr,
        cno = nilfs->ns_cno;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", cno);
+       return sysfs_emit(buf, "%llu\n", cno);
 }
 
 static ssize_t
@@ -575,7 +575,7 @@ nilfs_segctor_last_seg_write_time_secs_show(struct nilfs_segctor_attr *attr,
        ctime = nilfs->ns_ctime;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", ctime);
+       return sysfs_emit(buf, "%llu\n", ctime);
 }
 
 static ssize_t
@@ -603,7 +603,7 @@ nilfs_segctor_last_nongc_write_time_secs_show(struct nilfs_segctor_attr *attr,
        nongc_ctime = nilfs->ns_nongc_ctime;
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", nongc_ctime);
+       return sysfs_emit(buf, "%llu\n", nongc_ctime);
 }
 
 static ssize_t
@@ -617,7 +617,7 @@ nilfs_segctor_dirty_data_blocks_count_show(struct nilfs_segctor_attr *attr,
        ndirtyblks = atomic_read(&nilfs->ns_ndirtyblks);
        up_read(&nilfs->ns_segctor_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", ndirtyblks);
+       return sysfs_emit(buf, "%u\n", ndirtyblks);
 }
 
 static const char segctor_readme_str[] =
@@ -654,7 +654,7 @@ static ssize_t
 nilfs_segctor_README_show(struct nilfs_segctor_attr *attr,
                          struct the_nilfs *nilfs, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, segctor_readme_str);
+       return sysfs_emit(buf, segctor_readme_str);
 }
 
 NILFS_SEGCTOR_RO_ATTR(last_pseg_block);
@@ -723,7 +723,7 @@ nilfs_superblock_sb_write_time_secs_show(struct nilfs_superblock_attr *attr,
        sbwtime = nilfs->ns_sbwtime;
        up_read(&nilfs->ns_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", sbwtime);
+       return sysfs_emit(buf, "%llu\n", sbwtime);
 }
 
 static ssize_t
@@ -737,7 +737,7 @@ nilfs_superblock_sb_write_count_show(struct nilfs_superblock_attr *attr,
        sbwcount = nilfs->ns_sbwcount;
        up_read(&nilfs->ns_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", sbwcount);
+       return sysfs_emit(buf, "%u\n", sbwcount);
 }
 
 static ssize_t
@@ -751,7 +751,7 @@ nilfs_superblock_sb_update_frequency_show(struct nilfs_superblock_attr *attr,
        sb_update_freq = nilfs->ns_sb_update_freq;
        up_read(&nilfs->ns_sem);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", sb_update_freq);
+       return sysfs_emit(buf, "%u\n", sb_update_freq);
 }
 
 static ssize_t
@@ -799,7 +799,7 @@ static ssize_t
 nilfs_superblock_README_show(struct nilfs_superblock_attr *attr,
                                struct the_nilfs *nilfs, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, sb_readme_str);
+       return sysfs_emit(buf, sb_readme_str);
 }
 
 NILFS_SUPERBLOCK_RO_ATTR(sb_write_time);
@@ -834,7 +834,7 @@ ssize_t nilfs_dev_revision_show(struct nilfs_dev_attr *attr,
        u32 major = le32_to_cpu(sbp[0]->s_rev_level);
        u16 minor = le16_to_cpu(sbp[0]->s_minor_rev_level);
 
-       return snprintf(buf, PAGE_SIZE, "%d.%d\n", major, minor);
+       return sysfs_emit(buf, "%d.%d\n", major, minor);
 }
 
 static
@@ -842,7 +842,7 @@ ssize_t nilfs_dev_blocksize_show(struct nilfs_dev_attr *attr,
                                 struct the_nilfs *nilfs,
                                 char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%u\n", nilfs->ns_blocksize);
+       return sysfs_emit(buf, "%u\n", nilfs->ns_blocksize);
 }
 
 static
@@ -853,7 +853,7 @@ ssize_t nilfs_dev_device_size_show(struct nilfs_dev_attr *attr,
        struct nilfs_super_block **sbp = nilfs->ns_sbp;
        u64 dev_size = le64_to_cpu(sbp[0]->s_dev_size);
 
-       return snprintf(buf, PAGE_SIZE, "%llu\n", dev_size);
+       return sysfs_emit(buf, "%llu\n", dev_size);
 }
 
 static
@@ -864,7 +864,7 @@ ssize_t nilfs_dev_free_blocks_show(struct nilfs_dev_attr *attr,
        sector_t free_blocks = 0;
 
        nilfs_count_free_blocks(nilfs, &free_blocks);
-       return snprintf(buf, PAGE_SIZE, "%llu\n",
+       return sysfs_emit(buf, "%llu\n",
                        (unsigned long long)free_blocks);
 }
 
@@ -875,7 +875,7 @@ ssize_t nilfs_dev_uuid_show(struct nilfs_dev_attr *attr,
 {
        struct nilfs_super_block **sbp = nilfs->ns_sbp;
 
-       return snprintf(buf, PAGE_SIZE, "%pUb\n", sbp[0]->s_uuid);
+       return sysfs_emit(buf, "%pUb\n", sbp[0]->s_uuid);
 }
 
 static
@@ -903,7 +903,7 @@ static ssize_t nilfs_dev_README_show(struct nilfs_dev_attr *attr,
                                     struct the_nilfs *nilfs,
                                     char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, dev_readme_str);
+       return sysfs_emit(buf, dev_readme_str);
 }
 
 NILFS_DEV_RO_ATTR(revision);
@@ -1047,7 +1047,7 @@ void nilfs_sysfs_delete_device_group(struct the_nilfs *nilfs)
 static ssize_t nilfs_feature_revision_show(struct kobject *kobj,
                                            struct attribute *attr, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+       return sysfs_emit(buf, "%d.%d\n",
                        NILFS_CURRENT_REV, NILFS_MINOR_REV);
 }
 
@@ -1060,7 +1060,7 @@ static ssize_t nilfs_feature_README_show(struct kobject *kobj,
                                         struct attribute *attr,
                                         char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, features_readme_str);
+       return sysfs_emit(buf, features_readme_str);
 }
 
 NILFS_FEATURE_RO_ATTR(revision);
index d001eb8..78a87a0 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * sysfs.h - sysfs support declarations.
+ * Sysfs support declarations.
  *
  * Copyright (C) 2005-2014 Nippon Telegraph and Telephone Corporation.
  * Copyright (C) 2014 HGST, Inc., a Western Digital Company.
index 1bfcb5d..dd48a8f 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * the_nilfs.c - the_nilfs shared structure.
+ * the_nilfs shared structure.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 987c8ab..47c7dfb 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * the_nilfs.h - the_nilfs shared structure.
+ * the_nilfs shared structure.
  *
  * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
  *
index 1f39409..13eda8d 100644 (file)
@@ -1982,19 +1982,21 @@ static int pid_revalidate(struct dentry *dentry, unsigned int flags)
 {
        struct inode *inode;
        struct task_struct *task;
+       int ret = 0;
 
-       if (flags & LOOKUP_RCU)
-               return -ECHILD;
-
-       inode = d_inode(dentry);
-       task = get_proc_task(inode);
+       rcu_read_lock();
+       inode = d_inode_rcu(dentry);
+       if (!inode)
+               goto out;
+       task = pid_task(proc_pid(inode), PIDTYPE_PID);
 
        if (task) {
                pid_update_inode(task, inode);
-               put_task_struct(task);
-               return 1;
+               ret = 1;
        }
-       return 0;
+out:
+       rcu_read_unlock();
+       return ret;
 }
 
 static inline bool proc_inode_is_dead(struct inode *inode)
@@ -3802,7 +3804,10 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx)
             task = next_tid(task), ctx->pos++) {
                char name[10 + 1];
                unsigned int len;
+
                tid = task_pid_nr_ns(task, ns);
+               if (!tid)
+                       continue;       /* The task has just exited. */
                len = snprintf(name, sizeof(name), "%u", tid);
                if (!proc_fill_cache(file, ctx, name, len,
                                proc_task_instantiate, task, NULL)) {
index cdbbf81..30a3b66 100644 (file)
@@ -62,46 +62,75 @@ core_param(novmcoredd, vmcoredd_disabled, bool, 0);
 /* Device Dump Size */
 static size_t vmcoredd_orig_sz;
 
-/*
- * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
- * The called function has to take care of module refcounting.
- */
-static int (*oldmem_pfn_is_ram)(unsigned long pfn);
-
-int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn))
+static DECLARE_RWSEM(vmcore_cb_rwsem);
+/* List of registered vmcore callbacks. */
+static LIST_HEAD(vmcore_cb_list);
+/* Whether we had a surprise unregistration of a callback. */
+static bool vmcore_cb_unstable;
+/* Whether the vmcore has been opened once. */
+static bool vmcore_opened;
+
+void register_vmcore_cb(struct vmcore_cb *cb)
 {
-       if (oldmem_pfn_is_ram)
-               return -EBUSY;
-       oldmem_pfn_is_ram = fn;
-       return 0;
+       down_write(&vmcore_cb_rwsem);
+       INIT_LIST_HEAD(&cb->next);
+       list_add_tail(&cb->next, &vmcore_cb_list);
+       /*
+        * Registering a vmcore callback after the vmcore was opened is
+        * very unusual (e.g., manual driver loading).
+        */
+       if (vmcore_opened)
+               pr_warn_once("Unexpected vmcore callback registration\n");
+       up_write(&vmcore_cb_rwsem);
 }
-EXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram);
+EXPORT_SYMBOL_GPL(register_vmcore_cb);
 
-void unregister_oldmem_pfn_is_ram(void)
+void unregister_vmcore_cb(struct vmcore_cb *cb)
 {
-       oldmem_pfn_is_ram = NULL;
-       wmb();
+       down_write(&vmcore_cb_rwsem);
+       list_del(&cb->next);
+       /*
+        * Unregistering a vmcore callback after the vmcore was opened is
+        * very unusual (e.g., forced driver removal), but we cannot stop
+        * unregistering.
+        */
+       if (vmcore_opened) {
+               pr_warn_once("Unexpected vmcore callback unregistration\n");
+               vmcore_cb_unstable = true;
+       }
+       up_write(&vmcore_cb_rwsem);
 }
-EXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram);
+EXPORT_SYMBOL_GPL(unregister_vmcore_cb);
 
-static int pfn_is_ram(unsigned long pfn)
+static bool pfn_is_ram(unsigned long pfn)
 {
-       int (*fn)(unsigned long pfn);
-       /* pfn is ram unless fn() checks pagetype */
-       int ret = 1;
+       struct vmcore_cb *cb;
+       bool ret = true;
 
-       /*
-        * Ask hypervisor if the pfn is really ram.
-        * A ballooned page contains no data and reading from such a page
-        * will cause high load in the hypervisor.
-        */
-       fn = oldmem_pfn_is_ram;
-       if (fn)
-               ret = fn(pfn);
+       lockdep_assert_held_read(&vmcore_cb_rwsem);
+       if (unlikely(vmcore_cb_unstable))
+               return false;
+
+       list_for_each_entry(cb, &vmcore_cb_list, next) {
+               if (unlikely(!cb->pfn_is_ram))
+                       continue;
+               ret = cb->pfn_is_ram(cb, pfn);
+               if (!ret)
+                       break;
+       }
 
        return ret;
 }
 
+static int open_vmcore(struct inode *inode, struct file *file)
+{
+       down_read(&vmcore_cb_rwsem);
+       vmcore_opened = true;
+       up_read(&vmcore_cb_rwsem);
+
+       return 0;
+}
+
 /* Reads a page from the oldmem device from given offset. */
 ssize_t read_from_oldmem(char *buf, size_t count,
                         u64 *ppos, int userbuf,
@@ -117,6 +146,7 @@ ssize_t read_from_oldmem(char *buf, size_t count,
        offset = (unsigned long)(*ppos % PAGE_SIZE);
        pfn = (unsigned long)(*ppos / PAGE_SIZE);
 
+       down_read(&vmcore_cb_rwsem);
        do {
                if (count > (PAGE_SIZE - offset))
                        nr_bytes = PAGE_SIZE - offset;
@@ -124,7 +154,7 @@ ssize_t read_from_oldmem(char *buf, size_t count,
                        nr_bytes = count;
 
                /* If pfn is not ram, return zeros for sparse dump files */
-               if (pfn_is_ram(pfn) == 0)
+               if (!pfn_is_ram(pfn))
                        memset(buf, 0, nr_bytes);
                else {
                        if (encrypted)
@@ -136,8 +166,10 @@ ssize_t read_from_oldmem(char *buf, size_t count,
                                tmp = copy_oldmem_page(pfn, buf, nr_bytes,
                                                       offset, userbuf);
 
-                       if (tmp < 0)
+                       if (tmp < 0) {
+                               up_read(&vmcore_cb_rwsem);
                                return tmp;
+                       }
                }
                *ppos += nr_bytes;
                count -= nr_bytes;
@@ -147,6 +179,7 @@ ssize_t read_from_oldmem(char *buf, size_t count,
                offset = 0;
        } while (count);
 
+       up_read(&vmcore_cb_rwsem);
        return read;
 }
 
@@ -537,14 +570,19 @@ static int vmcore_remap_oldmem_pfn(struct vm_area_struct *vma,
                            unsigned long from, unsigned long pfn,
                            unsigned long size, pgprot_t prot)
 {
+       int ret;
+
        /*
         * Check if oldmem_pfn_is_ram was registered to avoid
         * looping over all pages without a reason.
         */
-       if (oldmem_pfn_is_ram)
-               return remap_oldmem_pfn_checked(vma, from, pfn, size, prot);
+       down_read(&vmcore_cb_rwsem);
+       if (!list_empty(&vmcore_cb_list) || vmcore_cb_unstable)
+               ret = remap_oldmem_pfn_checked(vma, from, pfn, size, prot);
        else
-               return remap_oldmem_pfn_range(vma, from, pfn, size, prot);
+               ret = remap_oldmem_pfn_range(vma, from, pfn, size, prot);
+       up_read(&vmcore_cb_rwsem);
+       return ret;
 }
 
 static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
@@ -668,6 +706,7 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
 #endif
 
 static const struct proc_ops vmcore_proc_ops = {
+       .proc_open      = open_vmcore,
        .proc_read      = read_vmcore,
        .proc_lseek     = default_llseek,
        .proc_mmap      = mmap_vmcore,
index e230234..bc66d01 100644 (file)
@@ -204,17 +204,20 @@ static int ramfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
        int opt;
 
        opt = fs_parse(fc, ramfs_fs_parameters, param, &result);
-       if (opt < 0) {
+       if (opt == -ENOPARAM) {
+               opt = vfs_parse_fs_param_source(fc, param);
+               if (opt != -ENOPARAM)
+                       return opt;
                /*
                 * We might like to report bad mount options here;
                 * but traditionally ramfs has ignored all mount options,
                 * and as it is used as a !CONFIG_SHMEM simple substitute
                 * for tmpfs, better continue to ignore other mount options.
                 */
-               if (opt == -ENOPARAM)
-                       opt = 0;
-               return opt;
+               return 0;
        }
+       if (opt < 0)
+               return opt;
 
        switch (opt) {
        case Opt_mode:
index 4a2cda0..f8e1f4e 100644 (file)
@@ -383,22 +383,6 @@ void seq_escape_mem(struct seq_file *m, const char *src, size_t len,
 }
 EXPORT_SYMBOL(seq_escape_mem);
 
-/**
- *     seq_escape -    print string into buffer, escaping some characters
- *     @m:     target buffer
- *     @s:     string
- *     @esc:   set of characters that need escaping
- *
- *     Puts string into buffer, replacing each occurrence of character from
- *     @esc with usual octal escape.
- *     Use seq_has_overflowed() to check for errors.
- */
-void seq_escape(struct seq_file *m, const char *s, const char *esc)
-{
-       seq_escape_str(m, s, ESCAPE_OCTAL, esc);
-}
-EXPORT_SYMBOL(seq_escape);
-
 void seq_vprintf(struct seq_file *m, const char *f, va_list args)
 {
        int len;
index cc8e2ed..d1def07 100644 (file)
@@ -474,10 +474,8 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent)
        struct sysv_sb_info *sbi;
        struct buffer_head *bh;
 
-       if (440 != sizeof (struct v7_super_block))
-               panic("V7 FS: bad super-block size");
-       if (64 != sizeof (struct sysv_inode))
-               panic("sysv fs: bad i-node size");
+       BUILD_BUG_ON(sizeof(struct v7_super_block) != 440);
+       BUILD_BUG_ON(sizeof(struct sysv_inode) != 64);
 
        sbi = kzalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
        if (!sbi)
index 596ab20..1dfadb2 100644 (file)
@@ -64,22 +64,6 @@ extern __visible const void __nosave_begin, __nosave_end;
 #define dereference_kernel_function_descriptor(p) ((void *)(p))
 #endif
 
-/* random extra sections (if any).  Override
- * in asm/sections.h */
-#ifndef arch_is_kernel_text
-static inline int arch_is_kernel_text(unsigned long addr)
-{
-       return 0;
-}
-#endif
-
-#ifndef arch_is_kernel_data
-static inline int arch_is_kernel_data(unsigned long addr)
-{
-       return 0;
-}
-#endif
-
 /**
  * memory_contains - checks if an object is contained within a memory region
  * @begin: virtual address of the beginning of the memory region
@@ -145,6 +129,22 @@ static inline bool init_section_intersects(void *virt, size_t size)
 }
 
 /**
+ * is_kernel_core_data - checks if the pointer address is located in the
+ *                      .data section
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in .data, false otherwise.
+ * Note: On some archs it may return true for core RODATA, and false
+ *       for others. But will always be true for core RW data.
+ */
+static inline bool is_kernel_core_data(unsigned long addr)
+{
+       return addr >= (unsigned long)_sdata &&
+              addr < (unsigned long)_edata;
+}
+
+/**
  * is_kernel_rodata - checks if the pointer address is located in the
  *                    .rodata section
  *
@@ -158,4 +158,47 @@ static inline bool is_kernel_rodata(unsigned long addr)
               addr < (unsigned long)__end_rodata;
 }
 
+/**
+ * is_kernel_inittext - checks if the pointer address is located in the
+ *                      .init.text section
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in .init.text, false otherwise.
+ */
+static inline bool is_kernel_inittext(unsigned long addr)
+{
+       return addr >= (unsigned long)_sinittext &&
+              addr < (unsigned long)_einittext;
+}
+
+/**
+ * __is_kernel_text - checks if the pointer address is located in the
+ *                    .text section
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in .text, false otherwise.
+ * Note: an internal helper, only check the range of _stext to _etext.
+ */
+static inline bool __is_kernel_text(unsigned long addr)
+{
+       return addr >= (unsigned long)_stext &&
+              addr < (unsigned long)_etext;
+}
+
+/**
+ * __is_kernel - checks if the pointer address is located in the kernel range
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in the kernel range, false otherwise.
+ * Note: an internal helper, only check the range of _stext to _end.
+ */
+static inline bool __is_kernel(unsigned long addr)
+{
+       return addr >= (unsigned long)_stext &&
+              addr < (unsigned long)_end;
+}
+
 #endif /* _ASM_GENERIC_SECTIONS_H_ */
index 018e776..b264007 100644 (file)
 
 #include <kunit/assert.h>
 #include <kunit/try-catch.h>
-#include <linux/kernel.h>
+
+#include <linux/container_of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kconfig.h>
+#include <linux/kref.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
 #include <linux/types.h>
-#include <linux/kref.h>
+
+#include <asm/rwonce.h>
 
 struct kunit_resource;
 
index eed86eb..fc53e0a 100644 (file)
@@ -2,6 +2,7 @@
 #ifndef _LINUX_BH_H
 #define _LINUX_BH_H
 
+#include <linux/instruction_pointer.h>
 #include <linux/preempt.h>
 
 #if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_TRACE_IRQFLAGS)
diff --git a/include/linux/container_of.h b/include/linux/container_of.h
new file mode 100644 (file)
index 0000000..2f4944b
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CONTAINER_OF_H
+#define _LINUX_CONTAINER_OF_H
+
+#include <linux/build_bug.h>
+#include <linux/err.h>
+
+#define typeof_member(T, m)    typeof(((T*)0)->m)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                             \
+       void *__mptr = (void *)(ptr);                                   \
+       static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
+                     __same_type(*(ptr), void),                        \
+                     "pointer type mismatch in container_of()");       \
+       ((type *)(__mptr - offsetof(type, member))); })
+
+/**
+ * container_of_safe - cast a member of a structure out to the containing structure
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ * If IS_ERR_OR_NULL(ptr), ptr is returned unchanged.
+ */
+#define container_of_safe(ptr, type, member) ({                                \
+       void *__mptr = (void *)(ptr);                                   \
+       static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
+                     __same_type(*(ptr), void),                        \
+                     "pointer type mismatch in container_of_safe()");  \
+       IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) :                     \
+               ((type *)(__mptr - offsetof(type, member))); })
+
+#endif /* _LINUX_CONTAINER_OF_H */
index 2618577..6208215 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/pgtable.h>
 #include <uapi/linux/vmcore.h>
 
-#include <linux/pgtable.h> /* for pgprot_t */
-
 /* For IS_ENABLED(CONFIG_CRASH_DUMP) */
 #define ELFCORE_ADDR_MAX       (-1ULL)
 #define ELFCORE_ADDR_ERR       (-2ULL)
@@ -91,12 +89,32 @@ static inline void vmcore_unusable(void)
                elfcorehdr_addr = ELFCORE_ADDR_ERR;
 }
 
-#define HAVE_OLDMEM_PFN_IS_RAM 1
-extern int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn));
-extern void unregister_oldmem_pfn_is_ram(void);
+/**
+ * struct vmcore_cb - driver callbacks for /proc/vmcore handling
+ * @pfn_is_ram: check whether a PFN really is RAM and should be accessed when
+ *              reading the vmcore. Will return "true" if it is RAM or if the
+ *              callback cannot tell. If any callback returns "false", it's not
+ *              RAM and the page must not be accessed; zeroes should be
+ *              indicated in the vmcore instead. For example, a ballooned page
+ *              contains no data and reading from such a page will cause high
+ *              load in the hypervisor.
+ * @next: List head to manage registered callbacks internally; initialized by
+ *        register_vmcore_cb().
+ *
+ * vmcore callbacks allow drivers managing physical memory ranges to
+ * coordinate with vmcore handling code, for example, to prevent accessing
+ * physical memory ranges that should not be accessed when reading the vmcore,
+ * although included in the vmcore header as memory ranges to dump.
+ */
+struct vmcore_cb {
+       bool (*pfn_is_ram)(struct vmcore_cb *cb, unsigned long pfn);
+       struct list_head next;
+};
+extern void register_vmcore_cb(struct vmcore_cb *cb);
+extern void unregister_vmcore_cb(struct vmcore_cb *cb);
 
 #else /* !CONFIG_CRASH_DUMP */
-static inline bool is_kdump_kernel(void) { return 0; }
+static inline bool is_kdump_kernel(void) { return false; }
 #endif /* CONFIG_CRASH_DUMP */
 
 /* Device Dump information to be filled by drivers */
index 1d0e2ce..8eacf67 100644 (file)
@@ -19,7 +19,7 @@
  *   https://lists.openwall.net/linux-kernel/2011/01/09/56
  */
 
-#include <linux/kernel.h>
+#include <linux/math.h>
 
 extern unsigned long loops_per_jiffy;
 
index 4137a9b..3afca82 100644 (file)
@@ -3193,6 +3193,7 @@ static inline void remove_inode_hash(struct inode *inode)
 }
 
 extern void inode_sb_list_add(struct inode *inode);
+extern void inode_add_lru(struct inode *inode);
 
 extern int sb_set_blocksize(struct super_block *, int);
 extern int sb_min_blocksize(struct super_block *, int);
index bfd0032..107613f 100644 (file)
@@ -38,8 +38,9 @@
 
 #include <asm/page.h>
 #include <linux/bug.h>
-#include <linux/kernel.h>
 #include <linux/log2.h>
+#include <linux/math.h>
+#include <linux/types.h>
 
 struct genradix_root;
 
index 44c2ab0..00351cc 100644 (file)
@@ -477,8 +477,7 @@ static inline struct hugetlbfs_inode_info *HUGETLBFS_I(struct inode *inode)
 extern const struct file_operations hugetlbfs_file_operations;
 extern const struct vm_operations_struct hugetlb_vm_ops;
 struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
-                               struct ucounts **ucounts, int creat_flags,
-                               int page_size_log);
+                               int creat_flags, int page_size_log);
 
 static inline bool is_file_hugepages(struct file *file)
 {
@@ -497,8 +496,7 @@ static inline struct hstate *hstate_inode(struct inode *i)
 #define is_file_hugepages(file)                        false
 static inline struct file *
 hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
-               struct ucounts **ucounts, int creat_flags,
-               int page_size_log)
+               int creat_flags, int page_size_log)
 {
        return ERR_PTR(-ENOSYS);
 }
diff --git a/include/linux/instruction_pointer.h b/include/linux/instruction_pointer.h
new file mode 100644 (file)
index 0000000..cda1f70
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_INSTRUCTION_POINTER_H
+#define _LINUX_INSTRUCTION_POINTER_H
+
+#define _RET_IP_               (unsigned long)__builtin_return_address(0)
+#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })
+
+#endif /* _LINUX_INSTRUCTION_POINTER_H */
index a1d6fc8..4176c7e 100644 (file)
 struct cred;
 struct module;
 
-static inline int is_kernel_inittext(unsigned long addr)
-{
-       if (addr >= (unsigned long)_sinittext
-           && addr <= (unsigned long)_einittext)
-               return 1;
-       return 0;
-}
-
 static inline int is_kernel_text(unsigned long addr)
 {
-       if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) ||
-           arch_is_kernel_text(addr))
+       if (__is_kernel_text(addr))
                return 1;
        return in_gate_area_no_mm(addr);
 }
 
 static inline int is_kernel(unsigned long addr)
 {
-       if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
+       if (__is_kernel(addr))
                return 1;
        return in_gate_area_no_mm(addr);
 }
index e5359b0..968b4c4 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/stddef.h>
 #include <linux/types.h>
 #include <linux/compiler.h>
+#include <linux/container_of.h>
 #include <linux/bitops.h>
 #include <linux/kstrtox.h>
 #include <linux/log2.h>
@@ -19,6 +20,7 @@
 #include <linux/printk.h>
 #include <linux/build_bug.h>
 #include <linux/static_call_types.h>
+#include <linux/instruction_pointer.h>
 #include <asm/byteorder.h>
 
 #include <uapi/linux/kernel.h>
 }                                      \
 )
 
-#define typeof_member(T, m)    typeof(((T*)0)->m)
-
-#define _RET_IP_               (unsigned long)__builtin_return_address(0)
-#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })
-
 /**
  * upper_32_bits - return bits 32-63 of a number
  * @n: the number we're accessing
@@ -228,8 +225,6 @@ extern bool parse_option_str(const char *str, const char *option);
 extern char *next_arg(char *args, char **param, char **val);
 
 extern int core_kernel_text(unsigned long addr);
-extern int init_kernel_text(unsigned long addr);
-extern int core_kernel_data(unsigned long addr);
 extern int __kernel_text_address(unsigned long addr);
 extern int kernel_text_address(unsigned long addr);
 extern int func_ptr_is_kernel_text(void *ptr);
@@ -483,36 +478,6 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
 #define __CONCAT(a, b) a ## b
 #define CONCATENATE(a, b) __CONCAT(a, b)
 
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr:       the pointer to the member.
- * @type:      the type of the container struct this is embedded in.
- * @member:    the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({                             \
-       void *__mptr = (void *)(ptr);                                   \
-       BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
-                        !__same_type(*(ptr), void),                    \
-                        "pointer type mismatch in container_of()");    \
-       ((type *)(__mptr - offsetof(type, member))); })
-
-/**
- * container_of_safe - cast a member of a structure out to the containing structure
- * @ptr:       the pointer to the member.
- * @type:      the type of the container struct this is embedded in.
- * @member:    the name of the member within the struct.
- *
- * If IS_ERR_OR_NULL(ptr), ptr is returned unchanged.
- */
-#define container_of_safe(ptr, type, member) ({                                \
-       void *__mptr = (void *)(ptr);                                   \
-       BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
-                        !__same_type(*(ptr), void),                    \
-                        "pointer type mismatch in container_of()");    \
-       IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) :                     \
-               ((type *)(__mptr - offsetof(type, member))); })
-
 /* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */
 #ifdef CONFIG_FTRACE_MCOUNT_RECORD
 # define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD
index f2af4b4..6636fc0 100644 (file)
@@ -2,11 +2,13 @@
 #ifndef _LINUX_LIST_H
 #define _LINUX_LIST_H
 
+#include <linux/container_of.h>
 #include <linux/types.h>
 #include <linux/stddef.h>
 #include <linux/poison.h>
 #include <linux/const.h>
-#include <linux/kernel.h>
+
+#include <asm/barrier.h>
 
 /*
  * Circular doubly linked list implementation.
index 24f207b..85bda2d 100644 (file)
@@ -49,7 +49,9 @@
  */
 
 #include <linux/atomic.h>
-#include <linux/kernel.h>
+#include <linux/container_of.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
 
 struct llist_head {
        struct llist_node *first;
index db2c3e3..6a30916 100644 (file)
@@ -24,6 +24,56 @@ static inline bool mapping_empty(struct address_space *mapping)
 }
 
 /*
+ * mapping_shrinkable - test if page cache state allows inode reclaim
+ * @mapping: the page cache mapping
+ *
+ * This checks the mapping's cache state for the pupose of inode
+ * reclaim and LRU management.
+ *
+ * The caller is expected to hold the i_lock, but is not required to
+ * hold the i_pages lock, which usually protects cache state. That's
+ * because the i_lock and the list_lru lock that protect the inode and
+ * its LRU state don't nest inside the irq-safe i_pages lock.
+ *
+ * Cache deletions are performed under the i_lock, which ensures that
+ * when an inode goes empty, it will reliably get queued on the LRU.
+ *
+ * Cache additions do not acquire the i_lock and may race with this
+ * check, in which case we'll report the inode as shrinkable when it
+ * has cache pages. This is okay: the shrinker also checks the
+ * refcount and the referenced bit, which will be elevated or set in
+ * the process of adding new cache pages to an inode.
+ */
+static inline bool mapping_shrinkable(struct address_space *mapping)
+{
+       void *head;
+
+       /*
+        * On highmem systems, there could be lowmem pressure from the
+        * inodes before there is highmem pressure from the page
+        * cache. Make inodes shrinkable regardless of cache state.
+        */
+       if (IS_ENABLED(CONFIG_HIGHMEM))
+               return true;
+
+       /* Cache completely empty? Shrink away. */
+       head = rcu_access_pointer(mapping->i_pages.xa_head);
+       if (!head)
+               return true;
+
+       /*
+        * The xarray stores single offset-0 entries directly in the
+        * head pointer, which allows non-resident page cache entries
+        * to escape the shadow shrinker's list of xarray nodes. The
+        * inode shrinker needs to pick them up under memory pressure.
+        */
+       if (!xa_is_node(head) && xa_is_value(head))
+               return true;
+
+       return false;
+}
+
+/*
  * Bits in mapping->flags.
  */
 enum mapping_flags {
index 66bab1b..0f352c1 100644 (file)
 #ifndef _LINUX_PLIST_H_
 #define _LINUX_PLIST_H_
 
-#include <linux/kernel.h>
+#include <linux/container_of.h>
 #include <linux/list.h>
+#include <linux/types.h>
+
+#include <asm/bug.h>
 
 struct plist_head {
        struct list_head node_list;
index 64ad900..f7c1d21 100644 (file)
@@ -9,8 +9,10 @@
 #define _LINUX_RADIX_TREE_H
 
 #include <linux/bitops.h>
-#include <linux/kernel.h>
+#include <linux/gfp.h>
 #include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/math.h>
 #include <linux/percpu.h>
 #include <linux/preempt.h>
 #include <linux/rcupdate.h>
index 352c612..f934876 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/linkage.h>
 
 #include <linux/types.h>
-#include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
index 4a6ff27..fc0357a 100644 (file)
@@ -9,8 +9,17 @@
 #ifndef __LINUX_SCALE_BITMAP_H
 #define __LINUX_SCALE_BITMAP_H
 
-#include <linux/kernel.h>
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/cache.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+#include <linux/minmax.h>
+#include <linux/percpu.h>
 #include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/wait.h>
 
 struct seq_file;
 
index dd99569..72dbb44 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/string_helpers.h>
 #include <linux/bug.h>
 #include <linux/mutex.h>
 #include <linux/cpumask.h>
@@ -135,7 +136,21 @@ static inline void seq_escape_str(struct seq_file *m, const char *src,
        seq_escape_mem(m, src, strlen(src), flags, esc);
 }
 
-void seq_escape(struct seq_file *m, const char *s, const char *esc);
+/**
+ * seq_escape - print string into buffer, escaping some characters
+ * @m: target buffer
+ * @s: NULL-terminated string
+ * @esc: set of characters that need escaping
+ *
+ * Puts string into buffer, replacing each occurrence of character from
+ * @esc with usual octal escape.
+ *
+ * Use seq_has_overflowed() to check for errors.
+ */
+static inline void seq_escape(struct seq_file *m, const char *s, const char *esc)
+{
+       seq_escape_str(m, s, ESCAPE_OCTAL, esc);
+}
 
 void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
                  int rowsize, int groupsize, const void *buf, size_t len,
@@ -194,7 +209,7 @@ static const struct file_operations __name ## _fops = {                     \
 #define DEFINE_PROC_SHOW_ATTRIBUTE(__name)                             \
 static int __name ## _open(struct inode *inode, struct file *file)     \
 {                                                                      \
-       return single_open(file, __name ## _show, inode->i_private);    \
+       return single_open(file, __name ## _show, PDE_DATA(inode));     \
 }                                                                      \
                                                                        \
 static const struct proc_ops __name ## _proc_ops = {                   \
index 7d34105..a6db6f2 100644 (file)
@@ -126,7 +126,6 @@ static inline int sigequalsets(const sigset_t *set1, const sigset_t *set2)
 #define sigmask(sig)   (1UL << ((sig) - 1))
 
 #ifndef __HAVE_ARCH_SIG_SETOPS
-#include <linux/string.h>
 
 #define _SIG_SET_BINOP(name, op)                                       \
 static inline void name(sigset_t *r, const sigset_t *a, const sigset_t *b) \
index 510519e..a80ab58 100644 (file)
@@ -108,7 +108,6 @@ static inline void on_each_cpu_cond(smp_cond_func_t cond_func,
 #ifdef CONFIG_SMP
 
 #include <linux/preempt.h>
-#include <linux/kernel.h>
 #include <linux/compiler.h>
 #include <linux/thread_info.h>
 #include <asm/smp.h>
index f044706..b4e5ca2 100644 (file)
@@ -57,7 +57,6 @@
 #include <linux/compiler.h>
 #include <linux/irqflags.h>
 #include <linux/thread_info.h>
-#include <linux/kernel.h>
 #include <linux/stringify.h>
 #include <linux/bottom_half.h>
 #include <linux/lockdep.h>
index d298609..c34b55a 100644 (file)
@@ -25,6 +25,11 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
 unsigned int stack_depot_fetch(depot_stack_handle_t handle,
                               unsigned long **entries);
 
+int stack_depot_snprint(depot_stack_handle_t handle, char *buf, size_t size,
+                      int spaces);
+
+void stack_depot_print(depot_stack_handle_t stack);
+
 #ifdef CONFIG_STACKDEPOT
 int stack_depot_init(void);
 #else
index 68189c4..4ba39e1 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/bits.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 struct file;
index 09737b4..fea489f 100644 (file)
 
 #include <linux/bitmap.h>
 #include <linux/bug.h>
+#include <linux/container_of.h>
 #include <linux/fwnode.h>
-#include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/media.h>
+#include <linux/types.h>
 
 /* Enums used internally at the media controller to represent graphs */
 
index 0c4a6e0..bb984ed 100644 (file)
@@ -915,7 +915,9 @@ static void __init print_unknown_bootoptions(void)
        for (p = &envp_init[2]; *p; p++)
                end += sprintf(end, " %s", *p);
 
-       pr_notice("Unknown command line parameters:%s\n", unknown_options);
+       /* Start at unknown_options[1] to skip the initial space */
+       pr_notice("Unknown kernel command line parameters \"%s\", will be passed to user space.\n",
+               &unknown_options[1]);
        memblock_free(unknown_options, len);
 }
 
index 3f312bf..f101c17 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/nsproxy.h>
 #include <linux/sysctl.h>
 #include <linux/uaccess.h>
+#include <linux/capability.h>
 #include <linux/ipc_namespace.h>
 #include <linux/msg.h>
 #include "util.h"
@@ -22,7 +23,6 @@ static void *get_ipc(struct ctl_table *table)
        return which;
 }
 
-#ifdef CONFIG_PROC_SYSCTL
 static int proc_ipc_dointvec(struct ctl_table *table, int write,
                void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -104,13 +104,17 @@ static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
        return ret;
 }
 
-#else
-#define proc_ipc_doulongvec_minmax NULL
-#define proc_ipc_dointvec         NULL
-#define proc_ipc_dointvec_minmax   NULL
-#define proc_ipc_dointvec_minmax_orphans   NULL
-#define proc_ipc_auto_msgmni      NULL
-#define proc_ipc_sem_dointvec     NULL
+#ifdef CONFIG_CHECKPOINT_RESTORE
+static int proc_ipc_dointvec_minmax_checkpoint_restore(struct ctl_table *table,
+               int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+       struct user_namespace *user_ns = current->nsproxy->ipc_ns->user_ns;
+
+       if (write && !checkpoint_restore_ns_capable(user_ns))
+               return -EPERM;
+
+       return proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+}
 #endif
 
 int ipc_mni = IPCMNI;
@@ -198,8 +202,8 @@ static struct ctl_table ipc_kern_table[] = {
                .procname       = "sem_next_id",
                .data           = &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
-               .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .mode           = 0666,
+               .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
                .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_INT_MAX,
        },
@@ -207,8 +211,8 @@ static struct ctl_table ipc_kern_table[] = {
                .procname       = "msg_next_id",
                .data           = &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
-               .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .mode           = 0666,
+               .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
                .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_INT_MAX,
        },
@@ -216,8 +220,8 @@ static struct ctl_table ipc_kern_table[] = {
                .procname       = "shm_next_id",
                .data           = &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
-               .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .mode           = 0666,
+               .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
                .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_INT_MAX,
        },
index ab749be..4942bdd 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -287,9 +287,6 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
        shm_unlock(shp);
        if (!is_file_hugepages(shm_file))
                shmem_lock(shm_file, 0, shp->mlock_ucounts);
-       else if (shp->mlock_ucounts)
-               user_shm_unlock(i_size_read(file_inode(shm_file)),
-                               shp->mlock_ucounts);
        fput(shm_file);
        ipc_update_pid(&shp->shm_cprid, NULL);
        ipc_update_pid(&shp->shm_lprid, NULL);
@@ -650,8 +647,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
                if (shmflg & SHM_NORESERVE)
                        acctflag = VM_NORESERVE;
                file = hugetlb_file_setup(name, hugesize, acctflag,
-                                 &shp->mlock_ucounts, HUGETLB_SHMFS_INODE,
-                               (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
+                               HUGETLB_SHMFS_INODE, (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
        } else {
                /*
                 * Do not allow no accounting for OVERCOMMIT_NEVER, even
@@ -698,8 +694,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 no_id:
        ipc_update_pid(&shp->shm_cprid, NULL);
        ipc_update_pid(&shp->shm_lprid, NULL);
-       if (is_file_hugepages(file) && shp->mlock_ucounts)
-               user_shm_unlock(size, shp->mlock_ucounts);
        fput(file);
        ipc_rcu_putref(&shp->shm_perm, shm_rcu_free);
        return error;
index 290661f..b6f330f 100644 (file)
@@ -62,40 +62,13 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
        return e;
 }
 
-int init_kernel_text(unsigned long addr)
-{
-       if (addr >= (unsigned long)_sinittext &&
-           addr < (unsigned long)_einittext)
-               return 1;
-       return 0;
-}
-
 int notrace core_kernel_text(unsigned long addr)
 {
-       if (addr >= (unsigned long)_stext &&
-           addr < (unsigned long)_etext)
+       if (is_kernel_text(addr))
                return 1;
 
        if (system_state < SYSTEM_FREEING_INITMEM &&
-           init_kernel_text(addr))
-               return 1;
-       return 0;
-}
-
-/**
- * core_kernel_data - tell if addr points to kernel data
- * @addr: address to test
- *
- * Returns true if @addr passed in is from the core kernel data
- * section.
- *
- * Note: On some archs it may return true for core RODATA, and false
- *  for others. But will always be true for core RW data.
- */
-int core_kernel_data(unsigned long addr)
-{
-       if (addr >= (unsigned long)_sdata &&
-           addr < (unsigned long)_edata)
+           is_kernel_inittext(addr))
                return 1;
        return 0;
 }
@@ -112,7 +85,7 @@ int __kernel_text_address(unsigned long addr)
         * Since we are after the module-symbols check, there's
         * no danger of address overlap:
         */
-       if (init_kernel_text(addr))
+       if (is_kernel_inittext(addr))
                return 1;
        return 0;
 }
index 3f112b1..5de23f3 100644 (file)
@@ -3024,7 +3024,7 @@ int unshare_fd(unsigned long unshare_flags, unsigned int max_fds,
 int ksys_unshare(unsigned long unshare_flags)
 {
        struct fs_struct *fs, *new_fs = NULL;
-       struct files_struct *fd, *new_fd = NULL;
+       struct files_struct *new_fd = NULL;
        struct cred *new_cred = NULL;
        struct nsproxy *new_nsproxy = NULL;
        int do_sysvsem = 0;
@@ -3111,11 +3111,8 @@ int ksys_unshare(unsigned long unshare_flags)
                        spin_unlock(&fs->lock);
                }
 
-               if (new_fd) {
-                       fd = current->files;
-                       current->files = new_fd;
-                       new_fd = fd;
-               }
+               if (new_fd)
+                       swap(current->files, new_fd);
 
                task_unlock(current);
 
index 80bfe71..36ca640 100644 (file)
@@ -88,6 +88,7 @@ static struct list_head kcov_remote_areas = LIST_HEAD_INIT(kcov_remote_areas);
 
 struct kcov_percpu_data {
        void                    *irq_area;
+       local_lock_t            lock;
 
        unsigned int            saved_mode;
        unsigned int            saved_size;
@@ -96,7 +97,9 @@ struct kcov_percpu_data {
        int                     saved_sequence;
 };
 
-static DEFINE_PER_CPU(struct kcov_percpu_data, kcov_percpu_data);
+static DEFINE_PER_CPU(struct kcov_percpu_data, kcov_percpu_data) = {
+       .lock = INIT_LOCAL_LOCK(lock),
+};
 
 /* Must be called with kcov_remote_lock locked. */
 static struct kcov_remote *kcov_remote_find(u64 handle)
@@ -824,7 +827,7 @@ void kcov_remote_start(u64 handle)
        if (!in_task() && !in_serving_softirq())
                return;
 
-       local_irq_save(flags);
+       local_lock_irqsave(&kcov_percpu_data.lock, flags);
 
        /*
         * Check that kcov_remote_start() is not called twice in background
@@ -832,7 +835,7 @@ void kcov_remote_start(u64 handle)
         */
        mode = READ_ONCE(t->kcov_mode);
        if (WARN_ON(in_task() && kcov_mode_enabled(mode))) {
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
        /*
@@ -841,14 +844,15 @@ void kcov_remote_start(u64 handle)
         * happened while collecting coverage from a background thread.
         */
        if (WARN_ON(in_serving_softirq() && t->kcov_softirq)) {
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
 
        spin_lock(&kcov_remote_lock);
        remote = kcov_remote_find(handle);
        if (!remote) {
-               spin_unlock_irqrestore(&kcov_remote_lock, flags);
+               spin_unlock(&kcov_remote_lock);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
        kcov_debug("handle = %llx, context: %s\n", handle,
@@ -869,19 +873,19 @@ void kcov_remote_start(u64 handle)
                size = CONFIG_KCOV_IRQ_AREA_SIZE;
                area = this_cpu_ptr(&kcov_percpu_data)->irq_area;
        }
-       spin_unlock_irqrestore(&kcov_remote_lock, flags);
+       spin_unlock(&kcov_remote_lock);
 
        /* Can only happen when in_task(). */
        if (!area) {
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                area = vmalloc(size * sizeof(unsigned long));
                if (!area) {
                        kcov_put(kcov);
                        return;
                }
+               local_lock_irqsave(&kcov_percpu_data.lock, flags);
        }
 
-       local_irq_save(flags);
-
        /* Reset coverage size. */
        *(u64 *)area = 0;
 
@@ -891,7 +895,7 @@ void kcov_remote_start(u64 handle)
        }
        kcov_start(t, kcov, size, area, mode, sequence);
 
-       local_irq_restore(flags);
+       local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
 
 }
 EXPORT_SYMBOL(kcov_remote_start);
@@ -965,12 +969,12 @@ void kcov_remote_stop(void)
        if (!in_task() && !in_serving_softirq())
                return;
 
-       local_irq_save(flags);
+       local_lock_irqsave(&kcov_percpu_data.lock, flags);
 
        mode = READ_ONCE(t->kcov_mode);
        barrier();
        if (!kcov_mode_enabled(mode)) {
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
        /*
@@ -978,12 +982,12 @@ void kcov_remote_stop(void)
         * actually found the remote handle and started collecting coverage.
         */
        if (in_serving_softirq() && !t->kcov_softirq) {
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
        /* Make sure that kcov_softirq is only set when in softirq. */
        if (WARN_ON(!in_serving_softirq() && t->kcov_softirq)) {
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
                return;
        }
 
@@ -1013,7 +1017,7 @@ void kcov_remote_stop(void)
                spin_unlock(&kcov_remote_lock);
        }
 
-       local_irq_restore(flags);
+       local_unlock_irqrestore(&kcov_percpu_data.lock, flags);
 
        /* Get in kcov_remote_start(). */
        kcov_put(kcov);
@@ -1034,8 +1038,8 @@ static int __init kcov_init(void)
        int cpu;
 
        for_each_possible_cpu(cpu) {
-               void *area = vmalloc(CONFIG_KCOV_IRQ_AREA_SIZE *
-                               sizeof(unsigned long));
+               void *area = vmalloc_node(CONFIG_KCOV_IRQ_AREA_SIZE *
+                               sizeof(unsigned long), cpu_to_node(cpu));
                if (!area)
                        return -ENOMEM;
                per_cpu_ptr(&kcov_percpu_data, cpu)->irq_area = area;
index 74d3716..2270ec6 100644 (file)
@@ -818,9 +818,6 @@ static int static_obj(const void *obj)
        if ((addr >= start) && (addr < end))
                return 1;
 
-       if (arch_is_kernel_data(addr))
-               return 1;
-
        /*
         * in-kernel percpu var?
         */
index ca9f519..5ad3eba 100644 (file)
@@ -73,6 +73,18 @@ static struct resource *next_resource(struct resource *p)
        return p->sibling;
 }
 
+static struct resource *next_resource_skip_children(struct resource *p)
+{
+       while (!p->sibling && p->parent)
+               p = p->parent;
+       return p->sibling;
+}
+
+#define for_each_resource(_root, _p, _skip_children) \
+       for ((_p) = (_root)->child; (_p); \
+            (_p) = (_skip_children) ? next_resource_skip_children(_p) : \
+                                      next_resource(_p))
+
 static void *r_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct resource *p = v;
@@ -1707,37 +1719,49 @@ static int strict_iomem_checks;
 #endif
 
 /*
- * check if an address is reserved in the iomem resource tree
- * returns true if reserved, false if not reserved.
+ * Check if an address is exclusive to the kernel and must not be mapped to
+ * user space, for example, via /dev/mem.
+ *
+ * Returns true if exclusive to the kernel, otherwise returns false.
  */
 bool iomem_is_exclusive(u64 addr)
 {
-       struct resource *p = &iomem_resource;
-       bool err = false;
-       loff_t l;
+       const unsigned int exclusive_system_ram = IORESOURCE_SYSTEM_RAM |
+                                                 IORESOURCE_EXCLUSIVE;
+       bool skip_children = false, err = false;
        int size = PAGE_SIZE;
-
-       if (!strict_iomem_checks)
-               return false;
+       struct resource *p;
 
        addr = addr & PAGE_MASK;
 
        read_lock(&resource_lock);
-       for (p = p->child; p ; p = r_next(NULL, p, &l)) {
-               /*
-                * We can probably skip the resources without
-                * IORESOURCE_IO attribute?
-                */
+       for_each_resource(&iomem_resource, p, skip_children) {
                if (p->start >= addr + size)
                        break;
-               if (p->end < addr)
+               if (p->end < addr) {
+                       skip_children = true;
                        continue;
+               }
+               skip_children = false;
+
+               /*
+                * IORESOURCE_SYSTEM_RAM resources are exclusive if
+                * IORESOURCE_EXCLUSIVE is set, even if they
+                * are not busy and even if "iomem=relaxed" is set. The
+                * responsible driver dynamically adds/removes system RAM within
+                * such an area and uncontrolled access is dangerous.
+                */
+               if ((p->flags & exclusive_system_ram) == exclusive_system_ram) {
+                       err = true;
+                       break;
+               }
+
                /*
                 * A resource is exclusive if IORESOURCE_EXCLUSIVE is set
                 * or CONFIG_IO_STRICT_DEVMEM is enabled and the
                 * resource is busy.
                 */
-               if ((p->flags & IORESOURCE_BUSY) == 0)
+               if (!strict_iomem_checks || !(p->flags & IORESOURCE_BUSY))
                        continue;
                if (IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)
                                || p->flags & IORESOURCE_EXCLUSIVE) {
index f3ea4e2..007a3de 100644 (file)
@@ -318,7 +318,7 @@ int __register_ftrace_function(struct ftrace_ops *ops)
        if (!ftrace_enabled && (ops->flags & FTRACE_OPS_FL_PERMANENT))
                return -EBUSY;
 
-       if (!core_kernel_data((unsigned long)ops))
+       if (!is_kernel_core_data((unsigned long)ops))
                ops->flags |= FTRACE_OPS_FL_DYNAMIC;
 
        add_ftrace_ops(&ftrace_ops_list, ops);
index abb3432..d5e82e4 100644 (file)
@@ -828,8 +828,7 @@ static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
  *   stops @miter.
  *
  * Context:
- *   Don't care if @miter is stopped, or not proceeded yet.
- *   Otherwise, preemption disabled if the SG_MITER_ATOMIC is set.
+ *   Don't care.
  *
  * Returns:
  *   true if @miter contains the valid mapping.  false if end of sg
@@ -865,8 +864,7 @@ EXPORT_SYMBOL(sg_miter_skip);
  *   @miter->addr and @miter->length point to the current mapping.
  *
  * Context:
- *   Preemption disabled if SG_MITER_ATOMIC.  Preemption must stay disabled
- *   till @miter is stopped.  May sleep if !SG_MITER_ATOMIC.
+ *   May sleep if !SG_MITER_ATOMIC.
  *
  * Returns:
  *   true if @miter contains the next mapping.  false if end of sg
@@ -906,8 +904,7 @@ EXPORT_SYMBOL(sg_miter_next);
  *   need to be released during iteration.
  *
  * Context:
- *   Preemption disabled if the SG_MITER_ATOMIC is set.  Don't care
- *   otherwise.
+ *   Don't care otherwise.
  */
 void sg_miter_stop(struct sg_mapping_iter *miter)
 {
@@ -922,7 +919,7 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
                        flush_dcache_page(miter->page);
 
                if (miter->__flags & SG_MITER_ATOMIC) {
-                       WARN_ON_ONCE(preemptible());
+                       WARN_ON_ONCE(!pagefault_disabled());
                        kunmap_atomic(miter->addr);
                } else
                        kunmap(miter->page);
index 09485dc..b437ae7 100644 (file)
@@ -214,6 +214,49 @@ static inline struct stack_record *find_stack(struct stack_record *bucket,
 }
 
 /**
+ * stack_depot_snprint - print stack entries from a depot into a buffer
+ *
+ * @handle:    Stack depot handle which was returned from
+ *             stack_depot_save().
+ * @buf:       Pointer to the print buffer
+ *
+ * @size:      Size of the print buffer
+ *
+ * @spaces:    Number of leading spaces to print
+ *
+ * Return:     Number of bytes printed.
+ */
+int stack_depot_snprint(depot_stack_handle_t handle, char *buf, size_t size,
+                      int spaces)
+{
+       unsigned long *entries;
+       unsigned int nr_entries;
+
+       nr_entries = stack_depot_fetch(handle, &entries);
+       return nr_entries ? stack_trace_snprint(buf, size, entries, nr_entries,
+                                               spaces) : 0;
+}
+EXPORT_SYMBOL_GPL(stack_depot_snprint);
+
+/**
+ * stack_depot_print - print stack entries from a depot
+ *
+ * @stack:             Stack depot handle which was returned from
+ *                     stack_depot_save().
+ *
+ */
+void stack_depot_print(depot_stack_handle_t stack)
+{
+       unsigned long *entries;
+       unsigned int nr_entries;
+
+       nr_entries = stack_depot_fetch(stack, &entries);
+       if (nr_entries > 0)
+               stack_trace_print(entries, nr_entries, 0);
+}
+EXPORT_SYMBOL_GPL(stack_depot_print);
+
+/**
  * stack_depot_fetch - Fetch stack entries from a depot
  *
  * @handle:            Stack depot handle which was returned from
@@ -231,6 +274,9 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle,
        struct stack_record *stack;
 
        *entries = NULL;
+       if (!handle)
+               return 0;
+
        if (parts.slabindex > depot_index) {
                WARN(1, "slab index %d out of bounds (%d) for stack id %08x\n",
                        parts.slabindex, depot_index, handle);
index f90f91d..58d5e56 100644 (file)
@@ -53,8 +53,7 @@
 #include <linux/string_helpers.h>
 #include "kstrtox.h"
 
-static unsigned long long simple_strntoull(const char *startp, size_t max_chars,
-                                          char **endp, unsigned int base)
+static noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base)
 {
        const char *cp;
        unsigned long long result = 0ULL;
index ae1f151..068ce59 100644 (file)
@@ -109,6 +109,13 @@ config NUMA_KEEP_MEMINFO
 config MEMORY_ISOLATION
        bool
 
+# IORESOURCE_SYSTEM_RAM regions in the kernel resource tree that are marked
+# IORESOURCE_EXCLUSIVE cannot be mapped to user space, for example, via
+# /dev/mem.
+config EXCLUSIVE_SYSTEM_RAM
+       def_bool y
+       depends on !DEVMEM || STRICT_DEVMEM
+
 #
 # Only be set on architectures that have completely implemented memory hotplug
 # feature. If you are not sure, don't touch it.
index 615512c..daa0e23 100644 (file)
@@ -261,9 +261,13 @@ void delete_from_page_cache(struct page *page)
        struct address_space *mapping = page_mapping(page);
 
        BUG_ON(!PageLocked(page));
+       spin_lock(&mapping->host->i_lock);
        xa_lock_irq(&mapping->i_pages);
        __delete_from_page_cache(page, NULL);
        xa_unlock_irq(&mapping->i_pages);
+       if (mapping_shrinkable(mapping))
+               inode_add_lru(mapping->host);
+       spin_unlock(&mapping->host->i_lock);
 
        page_cache_free_page(mapping, page);
 }
@@ -339,6 +343,7 @@ void delete_from_page_cache_batch(struct address_space *mapping,
        if (!pagevec_count(pvec))
                return;
 
+       spin_lock(&mapping->host->i_lock);
        xa_lock_irq(&mapping->i_pages);
        for (i = 0; i < pagevec_count(pvec); i++) {
                trace_mm_filemap_delete_from_page_cache(pvec->pages[i]);
@@ -347,6 +352,9 @@ void delete_from_page_cache_batch(struct address_space *mapping,
        }
        page_cache_delete_batch(mapping, pvec);
        xa_unlock_irq(&mapping->i_pages);
+       if (mapping_shrinkable(mapping))
+               inode_add_lru(mapping->host);
+       spin_unlock(&mapping->host->i_lock);
 
        for (i = 0; i < pagevec_count(pvec); i++)
                page_cache_free_page(mapping, pvec->pages[i]);
index 9da071a..0bc10f4 100644 (file)
@@ -132,20 +132,11 @@ static void end_report(unsigned long *flags, unsigned long addr)
        kasan_enable_current();
 }
 
-static void print_stack(depot_stack_handle_t stack)
-{
-       unsigned long *entries;
-       unsigned int nr_entries;
-
-       nr_entries = stack_depot_fetch(stack, &entries);
-       stack_trace_print(entries, nr_entries, 0);
-}
-
 static void print_track(struct kasan_track *track, const char *prefix)
 {
        pr_err("%s by task %u:\n", prefix, track->pid);
        if (track->stack) {
-               print_stack(track->stack);
+               stack_depot_print(track->stack);
        } else {
                pr_err("(stack is not available)\n");
        }
@@ -214,12 +205,12 @@ static void describe_object_stacks(struct kmem_cache *cache, void *object,
                return;
        if (alloc_meta->aux_stack[0]) {
                pr_err("Last potentially related work creation:\n");
-               print_stack(alloc_meta->aux_stack[0]);
+               stack_depot_print(alloc_meta->aux_stack[0]);
                pr_err("\n");
        }
        if (alloc_meta->aux_stack[1]) {
                pr_err("Second to last potentially related work creation:\n");
-               print_stack(alloc_meta->aux_stack[1]);
+               stack_depot_print(alloc_meta->aux_stack[1]);
                pr_err("\n");
        }
 #endif
@@ -235,7 +226,7 @@ static void describe_object(struct kmem_cache *cache, void *object,
 
 static inline bool kernel_or_module_addr(const void *addr)
 {
-       if (addr >= (void *)_stext && addr < (void *)_end)
+       if (is_kernel((unsigned long)addr))
                return true;
        if (is_module_address((unsigned long)addr))
                return true;
index 081dd33..9f80f16 100644 (file)
@@ -297,9 +297,7 @@ SYSCALL_DEFINE2(memfd_create,
        }
 
        if (flags & MFD_HUGETLB) {
-               struct ucounts *ucounts = NULL;
-
-               file = hugetlb_file_setup(name, 0, VM_NORESERVE, &ucounts,
+               file = hugetlb_file_setup(name, 0, VM_NORESERVE,
                                        HUGETLB_ANONHUGE_INODE,
                                        (flags >> MFD_HUGE_SHIFT) &
                                        MFD_HUGE_MASK);
index b22a07f..bfb0ea1 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1599,7 +1599,6 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
                        goto out_fput;
                }
        } else if (flags & MAP_HUGETLB) {
-               struct ucounts *ucounts = NULL;
                struct hstate *hs;
 
                hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);
@@ -1615,7 +1614,7 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
                 */
                file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
                                VM_NORESERVE,
-                               &ucounts, HUGETLB_ANONHUGE_INODE,
+                               HUGETLB_ANONHUGE_INODE,
                                (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);
                if (IS_ERR(file))
                        return PTR_ERR(file);
index 07b61cd..79936db 100644 (file)
@@ -329,8 +329,6 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
                depot_stack_handle_t handle)
 {
        int ret, pageblock_mt, page_mt;
-       unsigned long *entries;
-       unsigned int nr_entries;
        char *kbuf;
 
        count = min_t(size_t, count, PAGE_SIZE);
@@ -361,8 +359,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
        if (ret >= count)
                goto err;
 
-       nr_entries = stack_depot_fetch(handle, &entries);
-       ret += stack_trace_snprint(kbuf + ret, count - ret, entries, nr_entries, 0);
+       ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0);
        if (ret >= count)
                goto err;
 
@@ -394,8 +391,6 @@ void __dump_page_owner(const struct page *page)
        struct page_ext *page_ext = lookup_page_ext(page);
        struct page_owner *page_owner;
        depot_stack_handle_t handle;
-       unsigned long *entries;
-       unsigned int nr_entries;
        gfp_t gfp_mask;
        int mt;
 
@@ -423,20 +418,17 @@ void __dump_page_owner(const struct page *page)
                 page_owner->pid, page_owner->ts_nsec, page_owner->free_ts_nsec);
 
        handle = READ_ONCE(page_owner->handle);
-       if (!handle) {
+       if (!handle)
                pr_alert("page_owner allocation stack trace missing\n");
-       } else {
-               nr_entries = stack_depot_fetch(handle, &entries);
-               stack_trace_print(entries, nr_entries, 0);
-       }
+       else
+               stack_depot_print(handle);
 
        handle = READ_ONCE(page_owner->free_handle);
        if (!handle) {
                pr_alert("page_owner free stack trace missing\n");
        } else {
-               nr_entries = stack_depot_fetch(handle, &entries);
                pr_alert("page last free stack trace:\n");
-               stack_trace_print(entries, nr_entries, 0);
+               stack_depot_print(handle);
        }
 
        if (page_owner->last_migrate_reason != -1)
index 714eaf1..cc83a3f 100644 (file)
@@ -45,9 +45,13 @@ static inline void __clear_shadow_entry(struct address_space *mapping,
 static void clear_shadow_entry(struct address_space *mapping, pgoff_t index,
                               void *entry)
 {
+       spin_lock(&mapping->host->i_lock);
        xa_lock_irq(&mapping->i_pages);
        __clear_shadow_entry(mapping, index, entry);
        xa_unlock_irq(&mapping->i_pages);
+       if (mapping_shrinkable(mapping))
+               inode_add_lru(mapping->host);
+       spin_unlock(&mapping->host->i_lock);
 }
 
 /*
@@ -73,8 +77,10 @@ static void truncate_exceptional_pvec_entries(struct address_space *mapping,
                return;
 
        dax = dax_mapping(mapping);
-       if (!dax)
+       if (!dax) {
+               spin_lock(&mapping->host->i_lock);
                xa_lock_irq(&mapping->i_pages);
+       }
 
        for (i = j; i < pagevec_count(pvec); i++) {
                struct page *page = pvec->pages[i];
@@ -93,8 +99,12 @@ static void truncate_exceptional_pvec_entries(struct address_space *mapping,
                __clear_shadow_entry(mapping, index, page);
        }
 
-       if (!dax)
+       if (!dax) {
                xa_unlock_irq(&mapping->i_pages);
+               if (mapping_shrinkable(mapping))
+                       inode_add_lru(mapping->host);
+               spin_unlock(&mapping->host->i_lock);
+       }
        pvec->nr = j;
 }
 
@@ -567,6 +577,7 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
        if (page_has_private(page) && !try_to_release_page(page, GFP_KERNEL))
                return 0;
 
+       spin_lock(&mapping->host->i_lock);
        xa_lock_irq(&mapping->i_pages);
        if (PageDirty(page))
                goto failed;
@@ -574,6 +585,9 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
        BUG_ON(page_has_private(page));
        __delete_from_page_cache(page, NULL);
        xa_unlock_irq(&mapping->i_pages);
+       if (mapping_shrinkable(mapping))
+               inode_add_lru(mapping->host);
+       spin_unlock(&mapping->host->i_lock);
 
        if (mapping->a_ops->freepage)
                mapping->a_ops->freepage(page);
@@ -582,6 +596,7 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
        return 1;
 failed:
        xa_unlock_irq(&mapping->i_pages);
+       spin_unlock(&mapping->host->i_lock);
        return 0;
 }
 
index ef4a6dc..fb95846 100644 (file)
@@ -1205,6 +1205,8 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
        BUG_ON(!PageLocked(page));
        BUG_ON(mapping != page_mapping(page));
 
+       if (!PageSwapCache(page))
+               spin_lock(&mapping->host->i_lock);
        xa_lock_irq(&mapping->i_pages);
        /*
         * The non racy check for a busy page.
@@ -1273,6 +1275,9 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
                        shadow = workingset_eviction(page, target_memcg);
                __delete_from_page_cache(page, shadow);
                xa_unlock_irq(&mapping->i_pages);
+               if (mapping_shrinkable(mapping))
+                       inode_add_lru(mapping->host);
+               spin_unlock(&mapping->host->i_lock);
 
                if (freepage != NULL)
                        freepage(page);
@@ -1282,6 +1287,8 @@ static int __remove_mapping(struct address_space *mapping, struct page *page,
 
 cannot_free:
        xa_unlock_irq(&mapping->i_pages);
+       if (!PageSwapCache(page))
+               spin_unlock(&mapping->host->i_lock);
        return 0;
 }
 
index 109ab97..8c03afe 100644 (file)
@@ -543,6 +543,13 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
                goto out;
        }
 
+       if (!spin_trylock(&mapping->host->i_lock)) {
+               xa_unlock(&mapping->i_pages);
+               spin_unlock_irq(lru_lock);
+               ret = LRU_RETRY;
+               goto out;
+       }
+
        list_lru_isolate(lru, item);
        __dec_lruvec_kmem_state(node, WORKINGSET_NODES);
 
@@ -562,6 +569,9 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
 
 out_invalid:
        xa_unlock_irq(&mapping->i_pages);
+       if (mapping_shrinkable(mapping))
+               inode_add_lru(mapping->host);
+       spin_unlock(&mapping->host->i_lock);
        ret = LRU_REMOVED_RETRY;
 out:
        cond_resched();
index f6cb0d4..4b45ed6 100644 (file)
@@ -144,7 +144,7 @@ static void ensure_safe_net_sysctl(struct net *net, const char *path,
                addr = (unsigned long)ent->data;
                if (is_module_address(addr))
                        where = "module";
-               else if (core_kernel_data(addr))
+               else if (is_kernel_core_data(addr))
                        where = "kernel";
                else
                        continue;
index 88cb294..1784921 100755 (executable)
@@ -63,6 +63,7 @@ my $min_conf_desc_length = 4;
 my $spelling_file = "$D/spelling.txt";
 my $codespell = 0;
 my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $user_codespellfile = "";
 my $conststructsfile = "$D/const_structs.checkpatch";
 my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst";
 my $typedefsfile;
@@ -130,7 +131,7 @@ Options:
   --ignore-perl-version      override checking of perl version.  expect
                              runtime errors.
   --codespell                Use the codespell dictionary for spelling/typos
-                             (default:/usr/share/codespell/dictionary.txt)
+                             (default:$codespellfile)
   --codespellfile            Use this codespell dictionary
   --typedefsfile             Read additional types from this file
   --color[=WHEN]             Use colors 'always', 'never', or only when output
@@ -317,7 +318,7 @@ GetOptions(
        'debug=s'       => \%debug,
        'test-only=s'   => \$tst_only,
        'codespell!'    => \$codespell,
-       'codespellfile=s'       => \$codespellfile,
+       'codespellfile=s'       => \$user_codespellfile,
        'typedefsfile=s'        => \$typedefsfile,
        'color=s'       => \$color,
        'no-color'      => \$color,     #keep old behaviors of -nocolor
@@ -325,9 +326,32 @@ GetOptions(
        'kconfig-prefix=s'      => \${CONFIG_},
        'h|help'        => \$help,
        'version'       => \$help
-) or help(1);
+) or $help = 2;
+
+if ($user_codespellfile) {
+       # Use the user provided codespell file unconditionally
+       $codespellfile = $user_codespellfile;
+} elsif (!(-f $codespellfile)) {
+       # If /usr/share/codespell/dictionary.txt is not present, try to find it
+       # under codespell's install directory: <codespell_root>/data/dictionary.txt
+       if (($codespell || $help) && which("codespell") ne "" && which("python") ne "") {
+               my $python_codespell_dict = << "EOF";
+
+import os.path as op
+import codespell_lib
+codespell_dir = op.dirname(codespell_lib.__file__)
+codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt')
+print(codespell_file, end='')
+EOF
+
+               my $codespell_dict = `python -c "$python_codespell_dict" 2> /dev/null`;
+               $codespellfile = $codespell_dict if (-f $codespell_dict);
+       }
+}
 
-help(0) if ($help);
+# $help is 1 if either -h, --help or --version is passed as option - exitcode: 0
+# $help is 2 if invalid option is passed - exitcode: 1
+help($help - 1) if ($help);
 
 die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix));
 die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse);
@@ -4449,6 +4473,7 @@ sub process {
                        #   XXX(foo);
                        #   EXPORT_SYMBOL(something_foo);
                        my $name = $1;
+                       $name =~ s/^\s*($Ident).*/$1/;
                        if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ &&
                            $name =~ /^${Ident}_$2/) {
 #print "FOO C name<$name>\n";
index 1aae4f4..3980985 100644 (file)
@@ -54,7 +54,11 @@ sd_desc
 seq_operations
 sirfsoc_padmux
 snd_ac97_build_ops
+snd_pcm_ops
+snd_rawmidi_ops
 snd_soc_component_driver
+snd_soc_dai_ops
+snd_soc_ops
 soc_pcmcia_socket_ops
 stacktrace_ops
 sysfs_ops
index 08d264a..46f7542 100644 (file)
@@ -148,7 +148,8 @@ lx-symbols command."""
         # drop all current symbols and reload vmlinux
         orig_vmlinux = 'vmlinux'
         for obj in gdb.objfiles():
-            if obj.filename.endswith('vmlinux'):
+            if (obj.filename.endswith('vmlinux') or
+                obj.filename.endswith('vmlinux.debug')):
                 orig_vmlinux = obj.filename
         gdb.execute("symbol-file", to_string=True)
         gdb.execute("symbol-file {0}".format(orig_vmlinux))
index cc9c846..a9ba782 100644 (file)
@@ -33,9 +33,9 @@ tap_timeout()
 {
        # Make sure tests will time out if utility is available.
        if [ -x /usr/bin/timeout ] ; then
-               /usr/bin/timeout --foreground "$kselftest_timeout" "$1"
+               /usr/bin/timeout --foreground "$kselftest_timeout" $1
        else
-               "$1"
+               $1
        fi
 }
 
@@ -65,17 +65,25 @@ run_one()
 
        TEST_HDR_MSG="selftests: $DIR: $BASENAME_TEST"
        echo "# $TEST_HDR_MSG"
-       if [ ! -x "$TEST" ]; then
-               echo -n "# Warning: file $TEST is "
-               if [ ! -e "$TEST" ]; then
-                       echo "missing!"
-               else
-                       echo "not executable, correct this."
-               fi
+       if [ ! -e "$TEST" ]; then
+               echo "# Warning: file $TEST is missing!"
                echo "not ok $test_num $TEST_HDR_MSG"
        else
+               cmd="./$BASENAME_TEST"
+               if [ ! -x "$TEST" ]; then
+                       echo "# Warning: file $TEST is not executable"
+
+                       if [ $(head -n 1 "$TEST" | cut -c -2) = "#!" ]
+                       then
+                               interpreter=$(head -n 1 "$TEST" | cut -c 3-)
+                               cmd="$interpreter ./$BASENAME_TEST"
+                       else
+                               echo "not ok $test_num $TEST_HDR_MSG"
+                               return
+                       fi
+               fi
                cd `dirname $TEST` > /dev/null
-               ((((( tap_timeout ./$BASENAME_TEST 2>&1; echo $? >&3) |
+               ((((( tap_timeout "$cmd" 2>&1; echo $? >&3) |
                        tap_prefix >&4) 3>&1) |
                        (read xs; exit $xs)) 4>>"$logfile" &&
                echo "ok $test_num $TEST_HDR_MSG") ||
index 8f3e72e..c4e6a34 100644 (file)
@@ -11,6 +11,7 @@
 /proc-self-syscall
 /proc-self-wchan
 /proc-subset-pid
+/proc-tid0
 /proc-uptime-001
 /proc-uptime-002
 /read
index 1054e40..219fc61 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 CFLAGS += -Wall -O2 -Wno-unused-function
 CFLAGS += -D_GNU_SOURCE
+LDFLAGS += -pthread
 
 TEST_GEN_PROGS :=
 TEST_GEN_PROGS += fd-001-lookup
@@ -13,6 +14,7 @@ TEST_GEN_PROGS += proc-self-map-files-002
 TEST_GEN_PROGS += proc-self-syscall
 TEST_GEN_PROGS += proc-self-wchan
 TEST_GEN_PROGS += proc-subset-pid
+TEST_GEN_PROGS += proc-tid0
 TEST_GEN_PROGS += proc-uptime-001
 TEST_GEN_PROGS += proc-uptime-002
 TEST_GEN_PROGS += read
diff --git a/tools/testing/selftests/proc/proc-tid0.c b/tools/testing/selftests/proc/proc-tid0.c
new file mode 100644 (file)
index 0000000..58c1d7c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2021 Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+// Test that /proc/*/task never contains "0".
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+
+static pid_t pid = -1;
+
+static void atexit_hook(void)
+{
+       if (pid > 0) {
+               kill(pid, SIGKILL);
+       }
+}
+
+static void *f(void *_)
+{
+       return NULL;
+}
+
+static void sigalrm(int _)
+{
+       exit(0);
+}
+
+int main(void)
+{
+       pid = fork();
+       if (pid == 0) {
+               /* child */
+               while (1) {
+                       pthread_t pth;
+                       pthread_create(&pth, NULL, f, NULL);
+                       pthread_join(pth, NULL);
+               }
+       } else if (pid > 0) {
+               /* parent */
+               atexit(atexit_hook);
+
+               char buf[64];
+               snprintf(buf, sizeof(buf), "/proc/%u/task", pid);
+
+               signal(SIGALRM, sigalrm);
+               alarm(1);
+
+               while (1) {
+                       DIR *d = opendir(buf);
+                       struct dirent *de;
+                       while ((de = readdir(d))) {
+                               if (strcmp(de->d_name, "0") == 0) {
+                                       exit(1);
+                               }
+                       }
+                       closedir(d);
+               }
+
+               return 0;
+       } else {
+               perror("fork");
+               return 1;
+       }
+}