Merge branch 'akpm' (patches from Andrew)
Merge more updates from Andrew Morton: - the rest of MM - procfs updates - various misc things - more y2038 fixes - get_maintainer updates - lib/ updates - checkpatch updates - various epoll updates - autofs updates - hfsplus - some reiserfs work - fatfs updates - signal.c cleanups - ipc/ updates * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (166 commits) ipc/util.c: update return value of ipc_getref from int to bool ipc/util.c: further variable name cleanups ipc: simplify ipc initialization ipc: get rid of ids->tables_initialized hack lib/rhashtable: guarantee initial hashtable allocation lib/rhashtable: simplify bucket_table_alloc() ipc: drop ipc_lock() ipc/util.c: correct comment in ipc_obtain_object_check ipc: rename ipcctl_pre_down_nolock() ipc/util.c: use ipc_rcu_putref() for failues in ipc_addid() ipc: reorganize initialization of kern_ipc_perm.seq ipc: compute kern_ipc_perm.id under the ipc lock init/Kconfig: remove EXPERT from CHECKPOINT_RESTORE fs/sysv/inode.c: use ktime_get_real_seconds() for superblock stamp adfs: use timespec64 for time conversion kernel/sysctl.c: fix typos in comments drivers/rapidio/devices/rio_mport_cdev.c: remove redundant pointer md fork: don't copy inconsistent signal handler state to child signal: make get_signal() return bool signal: make sigkill_pending() return bool ...
This commit is contained in:
commit
cd9b44f907
182 changed files with 3198 additions and 2276 deletions
|
@ -1072,6 +1072,24 @@ PAGE_SIZE multiple when read back.
|
|||
high limit is used and monitored properly, this limit's
|
||||
utility is limited to providing the final safety net.
|
||||
|
||||
memory.oom.group
|
||||
A read-write single value file which exists on non-root
|
||||
cgroups. The default value is "0".
|
||||
|
||||
Determines whether the cgroup should be treated as
|
||||
an indivisible workload by the OOM killer. If set,
|
||||
all tasks belonging to the cgroup or to its descendants
|
||||
(if the memory cgroup is not a leaf cgroup) are killed
|
||||
together or not at all. This can be used to avoid
|
||||
partial kills to guarantee workload integrity.
|
||||
|
||||
Tasks with the OOM protection (oom_score_adj set to -1000)
|
||||
are treated as an exception and are never killed.
|
||||
|
||||
If the OOM killer is invoked in a cgroup, it's not going
|
||||
to kill any tasks outside of this cgroup, regardless
|
||||
memory.oom.group values of ancestor cgroups.
|
||||
|
||||
memory.events
|
||||
A read-only flat-keyed file which exists on non-root cgroups.
|
||||
The following entries are defined. Unless specified
|
||||
|
|
|
@ -3041,8 +3041,9 @@
|
|||
on: enable the feature
|
||||
|
||||
page_poison= [KNL] Boot-time parameter changing the state of
|
||||
poisoning on the buddy allocator.
|
||||
off: turn off poisoning
|
||||
poisoning on the buddy allocator, available with
|
||||
CONFIG_PAGE_POISONING=y.
|
||||
off: turn off poisoning (default)
|
||||
on: turn on poisoning
|
||||
|
||||
panic= [KNL] Kernel behaviour on panic: delay <timeout>
|
||||
|
|
|
@ -870,6 +870,7 @@ Committed_AS: 100056 kB
|
|||
VmallocTotal: 112216 kB
|
||||
VmallocUsed: 428 kB
|
||||
VmallocChunk: 111088 kB
|
||||
Percpu: 62080 kB
|
||||
HardwareCorrupted: 0 kB
|
||||
AnonHugePages: 49152 kB
|
||||
ShmemHugePages: 0 kB
|
||||
|
@ -962,6 +963,8 @@ Committed_AS: The amount of memory presently allocated on the system.
|
|||
VmallocTotal: total size of vmalloc memory area
|
||||
VmallocUsed: amount of vmalloc area which is used
|
||||
VmallocChunk: largest contiguous block of vmalloc area which is free
|
||||
Percpu: Memory allocated to the percpu allocator used to back percpu
|
||||
allocations. This stat excludes the cost of metadata.
|
||||
|
||||
..............................................................................
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ show up in /proc/sys/kernel:
|
|||
- hung_task_panic
|
||||
- hung_task_check_count
|
||||
- hung_task_timeout_secs
|
||||
- hung_task_check_interval_secs
|
||||
- hung_task_warnings
|
||||
- hyperv_record_panic_msg
|
||||
- kexec_load_disabled
|
||||
|
@ -355,7 +356,7 @@ This file shows up if CONFIG_DETECT_HUNG_TASK is enabled.
|
|||
|
||||
hung_task_timeout_secs:
|
||||
|
||||
Check interval. When a task in D state did not get scheduled
|
||||
When a task in D state did not get scheduled
|
||||
for more than this value report a warning.
|
||||
This file shows up if CONFIG_DETECT_HUNG_TASK is enabled.
|
||||
|
||||
|
@ -364,6 +365,18 @@ Possible values to set are in range {0..LONG_MAX/HZ}.
|
|||
|
||||
==============================================================
|
||||
|
||||
hung_task_check_interval_secs:
|
||||
|
||||
Hung task check interval. If hung task checking is enabled
|
||||
(see hung_task_timeout_secs), the check is done every
|
||||
hung_task_check_interval_secs seconds.
|
||||
This file shows up if CONFIG_DETECT_HUNG_TASK is enabled.
|
||||
|
||||
0 (default): means use hung_task_timeout_secs as checking interval.
|
||||
Possible values to set are in range {0..LONG_MAX/HZ}.
|
||||
|
||||
==============================================================
|
||||
|
||||
hung_task_warnings:
|
||||
|
||||
The maximum number of warnings to report. During a check interval
|
||||
|
@ -451,7 +464,8 @@ Notes:
|
|||
1) kernel doesn't guarantee, that new object will have desired id. So,
|
||||
it's up to userspace, how to handle an object with "wrong" id.
|
||||
2) Toggle with non-default value will be set back to -1 by kernel after
|
||||
successful IPC object allocation.
|
||||
successful IPC object allocation. If an IPC object allocation syscall
|
||||
fails, it is undefined if the value remains unmodified or is reset to -1.
|
||||
|
||||
==============================================================
|
||||
|
||||
|
|
|
@ -691,7 +691,7 @@ and don't use much of it.
|
|||
The default value is 0.
|
||||
|
||||
See Documentation/vm/overcommit-accounting.rst and
|
||||
mm/mmap.c::__vm_enough_memory() for more information.
|
||||
mm/util.c::__vm_enough_memory() for more information.
|
||||
|
||||
==============================================================
|
||||
|
||||
|
|
10
arch/Kconfig
10
arch/Kconfig
|
@ -841,6 +841,16 @@ config REFCOUNT_FULL
|
|||
against various use-after-free conditions that can be used in
|
||||
security flaw exploits.
|
||||
|
||||
config HAVE_ARCH_PREL32_RELOCATIONS
|
||||
bool
|
||||
help
|
||||
May be selected by an architecture if it supports place-relative
|
||||
32-bit relocations, both in the toolchain and in the module loader,
|
||||
in which case relative references can be used in special sections
|
||||
for PCI fixup, initcalls etc which are only half the size on 64 bit
|
||||
architectures, and don't require runtime relocation on relocatable
|
||||
kernels.
|
||||
|
||||
source "kernel/gcov/Kconfig"
|
||||
|
||||
source "scripts/gcc-plugins/Kconfig"
|
||||
|
|
|
@ -330,16 +330,15 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
|
|||
* atomic helpers. Insert it into the gate_vma so that it is visible
|
||||
* through ptrace and /proc/<pid>/mem.
|
||||
*/
|
||||
static struct vm_area_struct gate_vma = {
|
||||
.vm_start = 0xffff0000,
|
||||
.vm_end = 0xffff0000 + PAGE_SIZE,
|
||||
.vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC,
|
||||
};
|
||||
static struct vm_area_struct gate_vma;
|
||||
|
||||
static int __init gate_vma_init(void)
|
||||
{
|
||||
vma_init(&gate_vma, NULL);
|
||||
gate_vma.vm_page_prot = PAGE_READONLY_EXEC;
|
||||
gate_vma.vm_start = 0xffff0000;
|
||||
gate_vma.vm_end = 0xffff0000 + PAGE_SIZE;
|
||||
gate_vma.vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(gate_vma_init);
|
||||
|
|
|
@ -108,6 +108,7 @@ config ARM64
|
|||
select HAVE_ARCH_KGDB
|
||||
select HAVE_ARCH_MMAP_RND_BITS
|
||||
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
|
||||
select HAVE_ARCH_PREL32_RELOCATIONS
|
||||
select HAVE_ARCH_SECCOMP_FILTER
|
||||
select HAVE_ARCH_STACKLEAK
|
||||
select HAVE_ARCH_THREAD_STRUCT_WHITELIST
|
||||
|
|
|
@ -177,6 +177,7 @@ config PPC
|
|||
select HAVE_ARCH_KGDB
|
||||
select HAVE_ARCH_MMAP_RND_BITS
|
||||
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
|
||||
select HAVE_ARCH_PREL32_RELOCATIONS
|
||||
select HAVE_ARCH_SECCOMP_FILTER
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
select HAVE_CBPF_JIT if !PPC64
|
||||
|
|
|
@ -124,6 +124,7 @@ config X86
|
|||
select HAVE_ARCH_MMAP_RND_BITS if MMU
|
||||
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
|
||||
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
|
||||
select HAVE_ARCH_PREL32_RELOCATIONS
|
||||
select HAVE_ARCH_SECCOMP_FILTER
|
||||
select HAVE_ARCH_THREAD_STRUCT_WHITELIST
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
|
|
|
@ -23,11 +23,8 @@
|
|||
* _ctype[] in lib/ctype.c is needed by isspace() of linux/ctype.h.
|
||||
* While both lib/ctype.c and lib/cmdline.c will bring EXPORT_SYMBOL
|
||||
* which is meaningless and will cause compiling error in some cases.
|
||||
* So do not include linux/export.h and define EXPORT_SYMBOL(sym)
|
||||
* as empty.
|
||||
*/
|
||||
#define _LINUX_EXPORT_H
|
||||
#define EXPORT_SYMBOL(sym)
|
||||
#define __DISABLE_EXPORTS
|
||||
|
||||
#include "misc.h"
|
||||
#include "error.h"
|
||||
|
|
|
@ -8,5 +8,6 @@ generated-y += xen-hypercalls.h
|
|||
|
||||
generic-y += dma-contiguous.h
|
||||
generic-y += early_ioremap.h
|
||||
generic-y += export.h
|
||||
generic-y += mcs_spinlock.h
|
||||
generic-y += mm-arch-hooks.h
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifdef CONFIG_64BIT
|
||||
#define KSYM_ALIGN 16
|
||||
#endif
|
||||
#include <asm-generic/export.h>
|
|
@ -7305,8 +7305,9 @@ static void vcpu_load_eoi_exitmap(struct kvm_vcpu *vcpu)
|
|||
kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
|
||||
}
|
||||
|
||||
void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
|
||||
unsigned long start, unsigned long end)
|
||||
int kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
|
||||
unsigned long start, unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
unsigned long apic_address;
|
||||
|
||||
|
@ -7317,6 +7318,8 @@ void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
|
|||
apic_address = gfn_to_hva(kvm, APIC_DEFAULT_PHYS_BASE >> PAGE_SHIFT);
|
||||
if (start <= apic_address && apic_address < end)
|
||||
kvm_make_all_cpus_request(kvm, KVM_REQ_APIC_PAGE_RELOAD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)
|
||||
|
|
|
@ -337,6 +337,7 @@ static ssize_t backing_dev_store(struct device *dev,
|
|||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
char *file_name;
|
||||
size_t sz;
|
||||
struct file *backing_dev = NULL;
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
|
@ -357,7 +358,11 @@ static ssize_t backing_dev_store(struct device *dev,
|
|||
goto out;
|
||||
}
|
||||
|
||||
strlcpy(file_name, buf, len);
|
||||
strlcpy(file_name, buf, PATH_MAX);
|
||||
/* ignore trailing newline */
|
||||
sz = strlen(file_name);
|
||||
if (sz > 0 && file_name[sz - 1] == '\n')
|
||||
file_name[sz - 1] = 0x00;
|
||||
|
||||
backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0);
|
||||
if (IS_ERR(backing_dev)) {
|
||||
|
|
|
@ -24,6 +24,7 @@ KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
|
|||
-D__NO_FORTIFY \
|
||||
$(call cc-option,-ffreestanding) \
|
||||
$(call cc-option,-fno-stack-protector) \
|
||||
-D__DISABLE_EXPORTS
|
||||
|
||||
GCOV_PROFILE := n
|
||||
KASAN_SANITIZE := n
|
||||
|
|
|
@ -178,12 +178,18 @@ void amdgpu_mn_unlock(struct amdgpu_mn *mn)
|
|||
*
|
||||
* @amn: our notifier
|
||||
*/
|
||||
static void amdgpu_mn_read_lock(struct amdgpu_mn *amn)
|
||||
static int amdgpu_mn_read_lock(struct amdgpu_mn *amn, bool blockable)
|
||||
{
|
||||
mutex_lock(&amn->read_lock);
|
||||
if (blockable)
|
||||
mutex_lock(&amn->read_lock);
|
||||
else if (!mutex_trylock(&amn->read_lock))
|
||||
return -EAGAIN;
|
||||
|
||||
if (atomic_inc_return(&amn->recursion) == 1)
|
||||
down_read_non_owner(&amn->lock);
|
||||
mutex_unlock(&amn->read_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,10 +245,11 @@ static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node,
|
|||
* Block for operations on BOs to finish and mark pages as accessed and
|
||||
* potentially dirty.
|
||||
*/
|
||||
static void amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
|
||||
static int amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);
|
||||
struct interval_tree_node *it;
|
||||
|
@ -250,17 +257,28 @@ static void amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
|
|||
/* notification is exclusive, but interval is inclusive */
|
||||
end -= 1;
|
||||
|
||||
amdgpu_mn_read_lock(amn);
|
||||
/* TODO we should be able to split locking for interval tree and
|
||||
* amdgpu_mn_invalidate_node
|
||||
*/
|
||||
if (amdgpu_mn_read_lock(amn, blockable))
|
||||
return -EAGAIN;
|
||||
|
||||
it = interval_tree_iter_first(&amn->objects, start, end);
|
||||
while (it) {
|
||||
struct amdgpu_mn_node *node;
|
||||
|
||||
if (!blockable) {
|
||||
amdgpu_mn_read_unlock(amn);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
node = container_of(it, struct amdgpu_mn_node, it);
|
||||
it = interval_tree_iter_next(it, start, end);
|
||||
|
||||
amdgpu_mn_invalidate_node(node, start, end);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,10 +293,11 @@ static void amdgpu_mn_invalidate_range_start_gfx(struct mmu_notifier *mn,
|
|||
* necessitates evicting all user-mode queues of the process. The BOs
|
||||
* are restorted in amdgpu_mn_invalidate_range_end_hsa.
|
||||
*/
|
||||
static void amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
|
||||
static int amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct amdgpu_mn *amn = container_of(mn, struct amdgpu_mn, mn);
|
||||
struct interval_tree_node *it;
|
||||
|
@ -286,13 +305,19 @@ static void amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
|
|||
/* notification is exclusive, but interval is inclusive */
|
||||
end -= 1;
|
||||
|
||||
amdgpu_mn_read_lock(amn);
|
||||
if (amdgpu_mn_read_lock(amn, blockable))
|
||||
return -EAGAIN;
|
||||
|
||||
it = interval_tree_iter_first(&amn->objects, start, end);
|
||||
while (it) {
|
||||
struct amdgpu_mn_node *node;
|
||||
struct amdgpu_bo *bo;
|
||||
|
||||
if (!blockable) {
|
||||
amdgpu_mn_read_unlock(amn);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
node = container_of(it, struct amdgpu_mn_node, it);
|
||||
it = interval_tree_iter_next(it, start, end);
|
||||
|
||||
|
@ -304,6 +329,8 @@ static void amdgpu_mn_invalidate_range_start_hsa(struct mmu_notifier *mn,
|
|||
amdgpu_amdkfd_evict_userptr(mem, mm);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -112,10 +112,11 @@ static void del_object(struct i915_mmu_object *mo)
|
|||
mo->attached = false;
|
||||
}
|
||||
|
||||
static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
|
||||
static int i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct i915_mmu_notifier *mn =
|
||||
container_of(_mn, struct i915_mmu_notifier, mn);
|
||||
|
@ -124,7 +125,7 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
|
|||
LIST_HEAD(cancelled);
|
||||
|
||||
if (RB_EMPTY_ROOT(&mn->objects.rb_root))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/* interval ranges are inclusive, but invalidate range is exclusive */
|
||||
end--;
|
||||
|
@ -132,6 +133,10 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
|
|||
spin_lock(&mn->lock);
|
||||
it = interval_tree_iter_first(&mn->objects, start, end);
|
||||
while (it) {
|
||||
if (!blockable) {
|
||||
spin_unlock(&mn->lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* The mmu_object is released late when destroying the
|
||||
* GEM object so it is entirely possible to gain a
|
||||
* reference on an object in the process of being freed
|
||||
|
@ -154,6 +159,8 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
|
|||
|
||||
if (!list_empty(&cancelled))
|
||||
flush_workqueue(mn->wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mmu_notifier_ops i915_gem_userptr_notifier = {
|
||||
|
|
|
@ -118,19 +118,27 @@ static void radeon_mn_release(struct mmu_notifier *mn,
|
|||
* We block for all BOs between start and end to be idle and
|
||||
* unmap them by move them into system domain again.
|
||||
*/
|
||||
static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
|
||||
static int radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
|
||||
struct ttm_operation_ctx ctx = { false, false };
|
||||
struct interval_tree_node *it;
|
||||
int ret = 0;
|
||||
|
||||
/* notification is exclusive, but interval is inclusive */
|
||||
end -= 1;
|
||||
|
||||
mutex_lock(&rmn->lock);
|
||||
/* TODO we should be able to split locking for interval tree and
|
||||
* the tear down.
|
||||
*/
|
||||
if (blockable)
|
||||
mutex_lock(&rmn->lock);
|
||||
else if (!mutex_trylock(&rmn->lock))
|
||||
return -EAGAIN;
|
||||
|
||||
it = interval_tree_iter_first(&rmn->objects, start, end);
|
||||
while (it) {
|
||||
|
@ -138,6 +146,11 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
|
|||
struct radeon_bo *bo;
|
||||
long r;
|
||||
|
||||
if (!blockable) {
|
||||
ret = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
node = container_of(it, struct radeon_mn_node, it);
|
||||
it = interval_tree_iter_next(it, start, end);
|
||||
|
||||
|
@ -166,7 +179,10 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
|
|||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&rmn->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmu_notifier_ops radeon_mn_ops = {
|
||||
|
|
|
@ -186,6 +186,7 @@ static void ib_umem_notifier_release(struct mmu_notifier *mn,
|
|||
rbt_ib_umem_for_each_in_range(&context->umem_tree, 0,
|
||||
ULLONG_MAX,
|
||||
ib_umem_notifier_release_trampoline,
|
||||
true,
|
||||
NULL);
|
||||
up_read(&context->umem_rwsem);
|
||||
}
|
||||
|
@ -207,22 +208,31 @@ static int invalidate_range_start_trampoline(struct ib_umem *item, u64 start,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn,
|
||||
static int ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct ib_ucontext *context = container_of(mn, struct ib_ucontext, mn);
|
||||
int ret;
|
||||
|
||||
if (!context->invalidate_range)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (blockable)
|
||||
down_read(&context->umem_rwsem);
|
||||
else if (!down_read_trylock(&context->umem_rwsem))
|
||||
return -EAGAIN;
|
||||
|
||||
ib_ucontext_notifier_start_account(context);
|
||||
down_read(&context->umem_rwsem);
|
||||
rbt_ib_umem_for_each_in_range(&context->umem_tree, start,
|
||||
ret = rbt_ib_umem_for_each_in_range(&context->umem_tree, start,
|
||||
end,
|
||||
invalidate_range_start_trampoline, NULL);
|
||||
invalidate_range_start_trampoline,
|
||||
blockable, NULL);
|
||||
up_read(&context->umem_rwsem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int invalidate_range_end_trampoline(struct ib_umem *item, u64 start,
|
||||
|
@ -242,10 +252,15 @@ static void ib_umem_notifier_invalidate_range_end(struct mmu_notifier *mn,
|
|||
if (!context->invalidate_range)
|
||||
return;
|
||||
|
||||
/*
|
||||
* TODO: we currently bail out if there is any sleepable work to be done
|
||||
* in ib_umem_notifier_invalidate_range_start so we shouldn't really block
|
||||
* here. But this is ugly and fragile.
|
||||
*/
|
||||
down_read(&context->umem_rwsem);
|
||||
rbt_ib_umem_for_each_in_range(&context->umem_tree, start,
|
||||
end,
|
||||
invalidate_range_end_trampoline, NULL);
|
||||
invalidate_range_end_trampoline, true, NULL);
|
||||
up_read(&context->umem_rwsem);
|
||||
ib_ucontext_notifier_end_account(context);
|
||||
}
|
||||
|
@ -798,6 +813,7 @@ EXPORT_SYMBOL(ib_umem_odp_unmap_dma_pages);
|
|||
int rbt_ib_umem_for_each_in_range(struct rb_root_cached *root,
|
||||
u64 start, u64 last,
|
||||
umem_call_back cb,
|
||||
bool blockable,
|
||||
void *cookie)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
@ -809,6 +825,9 @@ int rbt_ib_umem_for_each_in_range(struct rb_root_cached *root,
|
|||
|
||||
for (node = rbt_ib_umem_iter_first(root, start, last - 1);
|
||||
node; node = next) {
|
||||
/* TODO move the blockable decision up to the callback */
|
||||
if (!blockable)
|
||||
return -EAGAIN;
|
||||
next = rbt_ib_umem_iter_next(node, start, last - 1);
|
||||
umem = container_of(node, struct ib_umem_odp, interval_tree);
|
||||
ret_val = cb(umem->umem, start, last, cookie) || ret_val;
|
||||
|
|
|
@ -67,9 +67,9 @@ struct mmu_rb_handler {
|
|||
|
||||
static unsigned long mmu_node_start(struct mmu_rb_node *);
|
||||
static unsigned long mmu_node_last(struct mmu_rb_node *);
|
||||
static void mmu_notifier_range_start(struct mmu_notifier *,
|
||||
static int mmu_notifier_range_start(struct mmu_notifier *,
|
||||
struct mm_struct *,
|
||||
unsigned long, unsigned long);
|
||||
unsigned long, unsigned long, bool);
|
||||
static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *,
|
||||
unsigned long, unsigned long);
|
||||
static void do_remove(struct mmu_rb_handler *handler,
|
||||
|
@ -284,10 +284,11 @@ void hfi1_mmu_rb_remove(struct mmu_rb_handler *handler,
|
|||
handler->ops->remove(handler->ops_arg, node);
|
||||
}
|
||||
|
||||
static void mmu_notifier_range_start(struct mmu_notifier *mn,
|
||||
static int mmu_notifier_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct mmu_rb_handler *handler =
|
||||
container_of(mn, struct mmu_rb_handler, mn);
|
||||
|
@ -313,6 +314,8 @@ static void mmu_notifier_range_start(struct mmu_notifier *mn,
|
|||
|
||||
if (added)
|
||||
queue_work(handler->wq, &handler->del_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -488,7 +488,7 @@ void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *imr)
|
|||
|
||||
down_read(&ctx->umem_rwsem);
|
||||
rbt_ib_umem_for_each_in_range(&ctx->umem_tree, 0, ULLONG_MAX,
|
||||
mr_leaf_free, imr);
|
||||
mr_leaf_free, true, imr);
|
||||
up_read(&ctx->umem_rwsem);
|
||||
|
||||
wait_event(imr->q_leaf_free, !atomic_read(&imr->num_leaf_free));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
config BCACHE
|
||||
tristate "Block device as cache"
|
||||
select CRC64
|
||||
---help---
|
||||
Allows a block device to be used as cache for other devices; uses
|
||||
a btree for indexing and the layout is optimized for SSDs.
|
||||
|
|
|
@ -279,134 +279,3 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group (Any
|
||||
* use permitted, subject to terms of PostgreSQL license; see.)
|
||||
|
||||
* If we have a 64-bit integer type, then a 64-bit CRC looks just like the
|
||||
* usual sort of implementation. (See Ross Williams' excellent introduction
|
||||
* A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS, available from
|
||||
* ftp://ftp.rocksoft.com/papers/crc_v3.txt or several other net sites.)
|
||||
* If we have no working 64-bit type, then fake it with two 32-bit registers.
|
||||
*
|
||||
* The present implementation is a normal (not "reflected", in Williams'
|
||||
* terms) 64-bit CRC, using initial all-ones register contents and a final
|
||||
* bit inversion. The chosen polynomial is borrowed from the DLT1 spec
|
||||
* (ECMA-182, available from http://www.ecma.ch/ecma1/STAND/ECMA-182.HTM):
|
||||
*
|
||||
* x^64 + x^62 + x^57 + x^55 + x^54 + x^53 + x^52 + x^47 + x^46 + x^45 +
|
||||
* x^40 + x^39 + x^38 + x^37 + x^35 + x^33 + x^32 + x^31 + x^29 + x^27 +
|
||||
* x^24 + x^23 + x^22 + x^21 + x^19 + x^17 + x^13 + x^12 + x^10 + x^9 +
|
||||
* x^7 + x^4 + x + 1
|
||||
*/
|
||||
|
||||
static const uint64_t crc_table[256] = {
|
||||
0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
|
||||
0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
|
||||
0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
|
||||
0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
|
||||
0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
|
||||
0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
|
||||
0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
|
||||
0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
|
||||
0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
|
||||
0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
|
||||
0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
|
||||
0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
|
||||
0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
|
||||
0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
|
||||
0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
|
||||
0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
|
||||
0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
|
||||
0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
|
||||
0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
|
||||
0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
|
||||
0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
|
||||
0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
|
||||
0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
|
||||
0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
|
||||
0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
|
||||
0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
|
||||
0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
|
||||
0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
|
||||
0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
|
||||
0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
|
||||
0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
|
||||
0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
|
||||
0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
|
||||
0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
|
||||
0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
|
||||
0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
|
||||
0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
|
||||
0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
|
||||
0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
|
||||
0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
|
||||
0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
|
||||
0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
|
||||
0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
|
||||
0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
|
||||
0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
|
||||
0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
|
||||
0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
|
||||
0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
|
||||
0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
|
||||
0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
|
||||
0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
|
||||
0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
|
||||
0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
|
||||
0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
|
||||
0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
|
||||
0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
|
||||
0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
|
||||
0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
|
||||
0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
|
||||
0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
|
||||
0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
|
||||
0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
|
||||
0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
|
||||
0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
|
||||
0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
|
||||
0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
|
||||
0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
|
||||
0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
|
||||
0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
|
||||
0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
|
||||
0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
|
||||
0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
|
||||
0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
|
||||
0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
|
||||
0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
|
||||
0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
|
||||
0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
|
||||
0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
|
||||
0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
|
||||
0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
|
||||
0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
|
||||
0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
|
||||
0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
|
||||
0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
|
||||
0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
|
||||
0x9AFCE626CE85B507ULL,
|
||||
};
|
||||
|
||||
uint64_t bch_crc64_update(uint64_t crc, const void *_data, size_t len)
|
||||
{
|
||||
const unsigned char *data = _data;
|
||||
|
||||
while (len--) {
|
||||
int i = ((int) (crc >> 56) ^ *data++) & 0xFF;
|
||||
crc = crc_table[i] ^ (crc << 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint64_t bch_crc64(const void *data, size_t len)
|
||||
{
|
||||
uint64_t crc = 0xffffffffffffffffULL;
|
||||
|
||||
crc = bch_crc64_update(crc, data, len);
|
||||
|
||||
return crc ^ 0xffffffffffffffffULL;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/ratelimit.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/crc64.h>
|
||||
|
||||
#include "closure.h"
|
||||
|
||||
|
@ -542,6 +543,22 @@ dup: \
|
|||
#define RB_PREV(ptr, member) \
|
||||
container_of_or_null(rb_prev(&(ptr)->member), typeof(*ptr), member)
|
||||
|
||||
static inline uint64_t bch_crc64(const void *p, size_t len)
|
||||
{
|
||||
uint64_t crc = 0xffffffffffffffffULL;
|
||||
|
||||
crc = crc64_be(crc, p, len);
|
||||
return crc ^ 0xffffffffffffffffULL;
|
||||
}
|
||||
|
||||
static inline uint64_t bch_crc64_update(uint64_t crc,
|
||||
const void *p,
|
||||
size_t len)
|
||||
{
|
||||
crc = crc64_be(crc, p, len);
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Does linear interpolation between powers of two */
|
||||
static inline unsigned fract_exp_two(unsigned x, unsigned fract_bits)
|
||||
{
|
||||
|
@ -561,8 +578,4 @@ static inline sector_t bdev_sectors(struct block_device *bdev)
|
|||
{
|
||||
return bdev->bd_inode->i_size >> 9;
|
||||
}
|
||||
|
||||
uint64_t bch_crc64_update(uint64_t, const void *, size_t);
|
||||
uint64_t bch_crc64(const void *, size_t);
|
||||
|
||||
#endif /* _BCACHE_UTIL_H */
|
||||
|
|
|
@ -200,15 +200,18 @@ static void scif_mmu_notifier_release(struct mmu_notifier *mn,
|
|||
schedule_work(&scif_info.misc_work);
|
||||
}
|
||||
|
||||
static void scif_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
|
||||
static int scif_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct scif_mmu_notif *mmn;
|
||||
|
||||
mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier);
|
||||
scif_rma_destroy_tcw(mmn, start, end - start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scif_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,
|
||||
|
|
|
@ -219,9 +219,10 @@ void gru_flush_all_tlb(struct gru_state *gru)
|
|||
/*
|
||||
* MMUOPS notifier callout functions
|
||||
*/
|
||||
static void gru_invalidate_range_start(struct mmu_notifier *mn,
|
||||
static int gru_invalidate_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
unsigned long start, unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct gru_mm_struct *gms = container_of(mn, struct gru_mm_struct,
|
||||
ms_notifier);
|
||||
|
@ -231,6 +232,8 @@ static void gru_invalidate_range_start(struct mmu_notifier *mn,
|
|||
gru_dbg(grudev, "gms %p, start 0x%lx, end 0x%lx, act %d\n", gms,
|
||||
start, end, atomic_read(&gms->ms_range_active));
|
||||
gru_flush_tlb_range(gms, start, end - start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gru_invalidate_range_end(struct mmu_notifier *mn,
|
||||
|
|
|
@ -66,9 +66,15 @@ static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
|
|||
f->vendor == (u16) PCI_ANY_ID) &&
|
||||
(f->device == dev->device ||
|
||||
f->device == (u16) PCI_ANY_ID)) {
|
||||
calltime = fixup_debug_start(dev, f->hook);
|
||||
f->hook(dev);
|
||||
fixup_debug_report(dev, calltime, f->hook);
|
||||
void (*hook)(struct pci_dev *dev);
|
||||
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
|
||||
hook = offset_to_ptr(&f->hook_offset);
|
||||
#else
|
||||
hook = f->hook;
|
||||
#endif
|
||||
calltime = fixup_debug_start(dev, hook);
|
||||
hook(dev);
|
||||
fixup_debug_report(dev, calltime, hook);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1006,7 +1006,6 @@ static int rio_mport_transfer_ioctl(struct file *filp, void __user *arg)
|
|||
static int rio_mport_wait_for_async_dma(struct file *filp, void __user *arg)
|
||||
{
|
||||
struct mport_cdev_priv *priv;
|
||||
struct mport_dev *md;
|
||||
struct rio_async_tx_wait w_param;
|
||||
struct mport_dma_req *req;
|
||||
dma_cookie_t cookie;
|
||||
|
@ -1016,7 +1015,6 @@ static int rio_mport_wait_for_async_dma(struct file *filp, void __user *arg)
|
|||
int ret;
|
||||
|
||||
priv = (struct mport_cdev_priv *)filp->private_data;
|
||||
md = priv->md;
|
||||
|
||||
if (unlikely(copy_from_user(&w_param, arg, sizeof(w_param))))
|
||||
return -EFAULT;
|
||||
|
|
|
@ -479,18 +479,25 @@ static const struct vm_operations_struct gntdev_vmops = {
|
|||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static bool in_range(struct gntdev_grant_map *map,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
if (!map->vma)
|
||||
return false;
|
||||
if (map->vma->vm_start >= end)
|
||||
return false;
|
||||
if (map->vma->vm_end <= start)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unmap_if_in_range(struct gntdev_grant_map *map,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long mstart, mend;
|
||||
int err;
|
||||
|
||||
if (!map->vma)
|
||||
return;
|
||||
if (map->vma->vm_start >= end)
|
||||
return;
|
||||
if (map->vma->vm_end <= start)
|
||||
return;
|
||||
mstart = max(start, map->vma->vm_start);
|
||||
mend = min(end, map->vma->vm_end);
|
||||
pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
|
||||
|
@ -503,21 +510,40 @@ static void unmap_if_in_range(struct gntdev_grant_map *map,
|
|||
WARN_ON(err);
|
||||
}
|
||||
|
||||
static void mn_invl_range_start(struct mmu_notifier *mn,
|
||||
static int mn_invl_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
unsigned long start, unsigned long end,
|
||||
bool blockable)
|
||||
{
|
||||
struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
|
||||
struct gntdev_grant_map *map;
|
||||
int ret = 0;
|
||||
|
||||
/* TODO do we really need a mutex here? */
|
||||
if (blockable)
|
||||
mutex_lock(&priv->lock);
|
||||
else if (!mutex_trylock(&priv->lock))
|
||||
return -EAGAIN;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
list_for_each_entry(map, &priv->maps, next) {
|
||||
if (in_range(map, start, end)) {
|
||||
ret = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
unmap_if_in_range(map, start, end);
|
||||
}
|
||||
list_for_each_entry(map, &priv->freeable_maps, next) {
|
||||
if (in_range(map, start, end)) {
|
||||
ret = -EAGAIN;
|
||||
goto out_unlock;
|
||||
}
|
||||
unmap_if_in_range(map, start, end);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mn_release(struct mmu_notifier *mn,
|
||||
|
|
|
@ -167,7 +167,7 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode)
|
|||
* of time to convert from RISC OS epoch to Unix epoch.
|
||||
*/
|
||||
static void
|
||||
adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)
|
||||
adfs_adfs2unix_time(struct timespec64 *tv, struct inode *inode)
|
||||
{
|
||||
unsigned int high, low;
|
||||
/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
|
||||
|
@ -195,11 +195,11 @@ adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)
|
|||
/* convert from RISC OS to Unix epoch */
|
||||
nsec -= nsec_unix_epoch_diff_risc_os_epoch;
|
||||
|
||||
*tv = ns_to_timespec(nsec);
|
||||
*tv = ns_to_timespec64(nsec);
|
||||
return;
|
||||
|
||||
cur_time:
|
||||
*tv = timespec64_to_timespec(current_time(inode));
|
||||
*tv = current_time(inode);
|
||||
return;
|
||||
|
||||
too_early:
|
||||
|
@ -242,7 +242,6 @@ adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
|
|||
struct inode *
|
||||
adfs_iget(struct super_block *sb, struct object_info *obj)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct inode *inode;
|
||||
|
||||
inode = new_inode(sb);
|
||||
|
@ -271,9 +270,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj)
|
|||
ADFS_I(inode)->stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
|
||||
|
||||
inode->i_mode = adfs_atts2mode(sb, inode);
|
||||
ts = timespec64_to_timespec(inode->i_mtime);
|
||||
adfs_adfs2unix_time(&ts, inode);
|
||||
inode->i_mtime = timespec_to_timespec64(ts);
|
||||
adfs_adfs2unix_time(&inode->i_mtime, inode);
|
||||
inode->i_atime = inode->i_mtime;
|
||||
inode->i_ctime = inode->i_mtime;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
/* This is the range of ioctl() numbers we claim as ours */
|
||||
#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
|
||||
|
@ -125,7 +126,8 @@ struct autofs_sb_info {
|
|||
|
||||
static inline struct autofs_sb_info *autofs_sbi(struct super_block *sb)
|
||||
{
|
||||
return (struct autofs_sb_info *)(sb->s_fs_info);
|
||||
return sb->s_magic != AUTOFS_SUPER_MAGIC ?
|
||||
NULL : (struct autofs_sb_info *)(sb->s_fs_info);
|
||||
}
|
||||
|
||||
static inline struct autofs_info *autofs_dentry_ino(struct dentry *dentry)
|
||||
|
@ -152,15 +154,9 @@ int autofs_expire_run(struct super_block *, struct vfsmount *,
|
|||
struct autofs_sb_info *,
|
||||
struct autofs_packet_expire __user *);
|
||||
int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int when);
|
||||
struct autofs_sb_info *sbi, unsigned int how);
|
||||
int autofs_expire_multi(struct super_block *, struct vfsmount *,
|
||||
struct autofs_sb_info *, int __user *);
|
||||
struct dentry *autofs_expire_direct(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int how);
|
||||
struct dentry *autofs_expire_indirect(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int how);
|
||||
|
||||
/* Device node initialization */
|
||||
|
||||
|
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
#include "autofs_i.h"
|
||||
|
||||
static unsigned long now;
|
||||
|
||||
/* Check if a dentry can be expired */
|
||||
static inline int autofs_can_expire(struct dentry *dentry,
|
||||
unsigned long timeout, int do_now)
|
||||
unsigned long timeout, unsigned int how)
|
||||
{
|
||||
struct autofs_info *ino = autofs_dentry_ino(dentry);
|
||||
|
||||
|
@ -22,16 +20,17 @@ static inline int autofs_can_expire(struct dentry *dentry,
|
|||
if (ino == NULL)
|
||||
return 0;
|
||||
|
||||
if (!do_now) {
|
||||
if (!(how & AUTOFS_EXP_IMMEDIATE)) {
|
||||
/* Too young to die */
|
||||
if (!timeout || time_after(ino->last_used + timeout, now))
|
||||
if (!timeout || time_after(ino->last_used + timeout, jiffies))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check a mount point for busyness */
|
||||
static int autofs_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
|
||||
static int autofs_mount_busy(struct vfsmount *mnt,
|
||||
struct dentry *dentry, unsigned int how)
|
||||
{
|
||||
struct dentry *top = dentry;
|
||||
struct path path = {.mnt = mnt, .dentry = dentry};
|
||||
|
@ -52,6 +51,12 @@ static int autofs_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* Not a submount, has a forced expire been requested */
|
||||
if (how & AUTOFS_EXP_FORCED) {
|
||||
status = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Update the expiry counter if fs is busy */
|
||||
if (!may_umount_tree(path.mnt)) {
|
||||
struct autofs_info *ino;
|
||||
|
@ -187,10 +192,14 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
|
|||
static int autofs_direct_busy(struct vfsmount *mnt,
|
||||
struct dentry *top,
|
||||
unsigned long timeout,
|
||||
int do_now)
|
||||
unsigned int how)
|
||||
{
|
||||
pr_debug("top %p %pd\n", top, top);
|
||||
|
||||
/* Forced expire, user space handles busy mounts */
|
||||
if (how & AUTOFS_EXP_FORCED)
|
||||
return 0;
|
||||
|
||||
/* If it's busy update the expiry counters */
|
||||
if (!may_umount_tree(mnt)) {
|
||||
struct autofs_info *ino;
|
||||
|
@ -202,7 +211,7 @@ static int autofs_direct_busy(struct vfsmount *mnt,
|
|||
}
|
||||
|
||||
/* Timeout of a direct mount is determined by its top dentry */
|
||||
if (!autofs_can_expire(top, timeout, do_now))
|
||||
if (!autofs_can_expire(top, timeout, how))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -215,7 +224,7 @@ static int autofs_direct_busy(struct vfsmount *mnt,
|
|||
static int autofs_tree_busy(struct vfsmount *mnt,
|
||||
struct dentry *top,
|
||||
unsigned long timeout,
|
||||
int do_now)
|
||||
unsigned int how)
|
||||
{
|
||||
struct autofs_info *top_ino = autofs_dentry_ino(top);
|
||||
struct dentry *p;
|
||||
|
@ -237,7 +246,7 @@ static int autofs_tree_busy(struct vfsmount *mnt,
|
|||
* If the fs is busy update the expiry counter.
|
||||
*/
|
||||
if (d_mountpoint(p)) {
|
||||
if (autofs_mount_busy(mnt, p)) {
|
||||
if (autofs_mount_busy(mnt, p, how)) {
|
||||
top_ino->last_used = jiffies;
|
||||
dput(p);
|
||||
return 1;
|
||||
|
@ -260,8 +269,12 @@ static int autofs_tree_busy(struct vfsmount *mnt,
|
|||
}
|
||||
}
|
||||
|
||||
/* Forced expire, user space handles busy mounts */
|
||||
if (how & AUTOFS_EXP_FORCED)
|
||||
return 0;
|
||||
|
||||
/* Timeout of a tree mount is ultimately determined by its top dentry */
|
||||
if (!autofs_can_expire(top, timeout, do_now))
|
||||
if (!autofs_can_expire(top, timeout, how))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -270,7 +283,7 @@ static int autofs_tree_busy(struct vfsmount *mnt,
|
|||
static struct dentry *autofs_check_leaves(struct vfsmount *mnt,
|
||||
struct dentry *parent,
|
||||
unsigned long timeout,
|
||||
int do_now)
|
||||
unsigned int how)
|
||||
{
|
||||
struct dentry *p;
|
||||
|
||||
|
@ -282,11 +295,17 @@ static struct dentry *autofs_check_leaves(struct vfsmount *mnt,
|
|||
|
||||
if (d_mountpoint(p)) {
|
||||
/* Can we umount this guy */
|
||||
if (autofs_mount_busy(mnt, p))
|
||||
if (autofs_mount_busy(mnt, p, how))
|
||||
continue;
|
||||
|
||||
/* This isn't a submount so if a forced expire
|
||||
* has been requested, user space handles busy
|
||||
* mounts */
|
||||
if (how & AUTOFS_EXP_FORCED)
|
||||
return p;
|
||||
|
||||
/* Can we expire this guy */
|
||||
if (autofs_can_expire(p, timeout, do_now))
|
||||
if (autofs_can_expire(p, timeout, how))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
@ -294,23 +313,21 @@ static struct dentry *autofs_check_leaves(struct vfsmount *mnt,
|
|||
}
|
||||
|
||||
/* Check if we can expire a direct mount (possibly a tree) */
|
||||
struct dentry *autofs_expire_direct(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
int how)
|
||||
static struct dentry *autofs_expire_direct(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
unsigned int how)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct dentry *root = dget(sb->s_root);
|
||||
int do_now = how & AUTOFS_EXP_IMMEDIATE;
|
||||
struct autofs_info *ino;
|
||||
unsigned long timeout;
|
||||
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
now = jiffies;
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
if (!autofs_direct_busy(mnt, root, timeout, do_now)) {
|
||||
if (!autofs_direct_busy(mnt, root, timeout, how)) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs_dentry_ino(root);
|
||||
/* No point expiring a pending mount */
|
||||
|
@ -321,7 +338,7 @@ struct dentry *autofs_expire_direct(struct super_block *sb,
|
|||
ino->flags |= AUTOFS_INF_WANT_EXPIRE;
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
synchronize_rcu();
|
||||
if (!autofs_direct_busy(mnt, root, timeout, do_now)) {
|
||||
if (!autofs_direct_busy(mnt, root, timeout, how)) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino->flags |= AUTOFS_INF_EXPIRING;
|
||||
init_completion(&ino->expire_complete);
|
||||
|
@ -346,10 +363,8 @@ struct dentry *autofs_expire_direct(struct super_block *sb,
|
|||
static struct dentry *should_expire(struct dentry *dentry,
|
||||
struct vfsmount *mnt,
|
||||
unsigned long timeout,
|
||||
int how)
|
||||
unsigned int how)
|
||||
{
|
||||
int do_now = how & AUTOFS_EXP_IMMEDIATE;
|
||||
int exp_leaves = how & AUTOFS_EXP_LEAVES;
|
||||
struct autofs_info *ino = autofs_dentry_ino(dentry);
|
||||
unsigned int ino_count;
|
||||
|
||||
|
@ -367,22 +382,33 @@ static struct dentry *should_expire(struct dentry *dentry,
|
|||
pr_debug("checking mountpoint %p %pd\n", dentry, dentry);
|
||||
|
||||
/* Can we umount this guy */
|
||||
if (autofs_mount_busy(mnt, dentry))
|
||||
if (autofs_mount_busy(mnt, dentry, how))
|
||||
return NULL;
|
||||
|
||||
/* This isn't a submount so if a forced expire
|
||||
* has been requested, user space handles busy
|
||||
* mounts */
|
||||
if (how & AUTOFS_EXP_FORCED)
|
||||
return dentry;
|
||||
|
||||
/* Can we expire this guy */
|
||||
if (autofs_can_expire(dentry, timeout, do_now))
|
||||
if (autofs_can_expire(dentry, timeout, how))
|
||||
return dentry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
|
||||
pr_debug("checking symlink %p %pd\n", dentry, dentry);
|
||||
|
||||
/* Forced expire, user space handles busy mounts */
|
||||
if (how & AUTOFS_EXP_FORCED)
|
||||
return dentry;
|
||||
|
||||
/*
|
||||
* A symlink can't be "busy" in the usual sense so
|
||||
* just check last used for expire timeout.
|
||||
*/
|
||||
if (autofs_can_expire(dentry, timeout, do_now))
|
||||
if (autofs_can_expire(dentry, timeout, how))
|
||||
return dentry;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -391,27 +417,33 @@ static struct dentry *should_expire(struct dentry *dentry,
|
|||
return NULL;
|
||||
|
||||
/* Case 2: tree mount, expire iff entire tree is not busy */
|
||||
if (!exp_leaves) {
|
||||
/* Path walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (d_count(dentry) > ino_count)
|
||||
return NULL;
|
||||
if (!(how & AUTOFS_EXP_LEAVES)) {
|
||||
/* Not a forced expire? */
|
||||
if (!(how & AUTOFS_EXP_FORCED)) {
|
||||
/* ref-walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (d_count(dentry) > ino_count)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!autofs_tree_busy(mnt, dentry, timeout, do_now))
|
||||
if (!autofs_tree_busy(mnt, dentry, timeout, how))
|
||||
return dentry;
|
||||
/*
|
||||
* Case 3: pseudo direct mount, expire individual leaves
|
||||
* (autofs-4.1).
|
||||
*/
|
||||
} else {
|
||||
/* Path walk currently on this dentry? */
|
||||
struct dentry *expired;
|
||||
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (d_count(dentry) > ino_count)
|
||||
return NULL;
|
||||
/* Not a forced expire? */
|
||||
if (!(how & AUTOFS_EXP_FORCED)) {
|
||||
/* ref-walk currently on this dentry? */
|
||||
ino_count = atomic_read(&ino->count) + 1;
|
||||
if (d_count(dentry) > ino_count)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
expired = autofs_check_leaves(mnt, dentry, timeout, do_now);
|
||||
expired = autofs_check_leaves(mnt, dentry, timeout, how);
|
||||
if (expired) {
|
||||
if (expired == dentry)
|
||||
dput(dentry);
|
||||
|
@ -427,10 +459,10 @@ static struct dentry *should_expire(struct dentry *dentry,
|
|||
* - it is unused by any user process
|
||||
* - it has been unused for exp_timeout time
|
||||
*/
|
||||
struct dentry *autofs_expire_indirect(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
int how)
|
||||
static struct dentry *autofs_expire_indirect(struct super_block *sb,
|
||||
struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi,
|
||||
unsigned int how)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct dentry *root = sb->s_root;
|
||||
|
@ -442,13 +474,10 @@ struct dentry *autofs_expire_indirect(struct super_block *sb,
|
|||
if (!root)
|
||||
return NULL;
|
||||
|
||||
now = jiffies;
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
dentry = NULL;
|
||||
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
||||
int flags = how;
|
||||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs_dentry_ino(dentry);
|
||||
if (ino->flags & AUTOFS_INF_WANT_EXPIRE) {
|
||||
|
@ -457,7 +486,7 @@ struct dentry *autofs_expire_indirect(struct super_block *sb,
|
|||
}
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
expired = should_expire(dentry, mnt, timeout, flags);
|
||||
expired = should_expire(dentry, mnt, timeout, how);
|
||||
if (!expired)
|
||||
continue;
|
||||
|
||||
|
@ -470,7 +499,7 @@ struct dentry *autofs_expire_indirect(struct super_block *sb,
|
|||
/* Make sure a reference is not taken on found if
|
||||
* things have changed.
|
||||
*/
|
||||
flags &= ~AUTOFS_EXP_LEAVES;
|
||||
how &= ~AUTOFS_EXP_LEAVES;
|
||||
found = should_expire(expired, mnt, timeout, how);
|
||||
if (!found || found != expired)
|
||||
/* Something has changed, continue */
|
||||
|
@ -575,7 +604,7 @@ int autofs_expire_run(struct super_block *sb,
|
|||
spin_lock(&sbi->fs_lock);
|
||||
ino = autofs_dentry_ino(dentry);
|
||||
/* avoid rapid-fire expire attempts if expiry fails */
|
||||
ino->last_used = now;
|
||||
ino->last_used = jiffies;
|
||||
ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
|
||||
complete_all(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
@ -584,15 +613,15 @@ int autofs_expire_run(struct super_block *sb,
|
|||
}
|
||||
|
||||
int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int when)
|
||||
struct autofs_sb_info *sbi, unsigned int how)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int ret = -EAGAIN;
|
||||
|
||||
if (autofs_type_trigger(sbi->type))
|
||||
dentry = autofs_expire_direct(sb, mnt, sbi, when);
|
||||
dentry = autofs_expire_direct(sb, mnt, sbi, how);
|
||||
else
|
||||
dentry = autofs_expire_indirect(sb, mnt, sbi, when);
|
||||
dentry = autofs_expire_indirect(sb, mnt, sbi, how);
|
||||
|
||||
if (dentry) {
|
||||
struct autofs_info *ino = autofs_dentry_ino(dentry);
|
||||
|
@ -605,7 +634,7 @@ int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
|||
|
||||
spin_lock(&sbi->fs_lock);
|
||||
/* avoid rapid-fire expire attempts if expiry fails */
|
||||
ino->last_used = now;
|
||||
ino->last_used = jiffies;
|
||||
ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
|
||||
complete_all(&ino->expire_complete);
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
@ -622,10 +651,10 @@ int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
|||
int autofs_expire_multi(struct super_block *sb, struct vfsmount *mnt,
|
||||
struct autofs_sb_info *sbi, int __user *arg)
|
||||
{
|
||||
int do_now = 0;
|
||||
unsigned int how = 0;
|
||||
|
||||
if (arg && get_user(do_now, arg))
|
||||
if (arg && get_user(how, arg))
|
||||
return -EFAULT;
|
||||
|
||||
return autofs_do_expire_multi(sb, mnt, sbi, do_now);
|
||||
return autofs_do_expire_multi(sb, mnt, sbi, how);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include "autofs_i.h"
|
||||
|
||||
|
|
|
@ -559,6 +559,13 @@ static int autofs_dir_symlink(struct inode *dir,
|
|||
if (!autofs_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
/* autofs_oz_mode() needs to allow path walks when the
|
||||
* autofs mount is catatonic but the state of an autofs
|
||||
* file system needs to be preserved over restarts.
|
||||
*/
|
||||
if (sbi->catatonic)
|
||||
return -EACCES;
|
||||
|
||||
BUG_ON(!ino);
|
||||
|
||||
autofs_clean_ino(ino);
|
||||
|
@ -612,9 +619,15 @@ static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
|
|||
struct autofs_info *ino = autofs_dentry_ino(dentry);
|
||||
struct autofs_info *p_ino;
|
||||
|
||||
/* This allows root to remove symlinks */
|
||||
if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (!autofs_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
/* autofs_oz_mode() needs to allow path walks when the
|
||||
* autofs mount is catatonic but the state of an autofs
|
||||
* file system needs to be preserved over restarts.
|
||||
*/
|
||||
if (sbi->catatonic)
|
||||
return -EACCES;
|
||||
|
||||
if (atomic_dec_and_test(&ino->count)) {
|
||||
p_ino = autofs_dentry_ino(dentry->d_parent);
|
||||
|
@ -697,6 +710,13 @@ static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
if (!autofs_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
/* autofs_oz_mode() needs to allow path walks when the
|
||||
* autofs mount is catatonic but the state of an autofs
|
||||
* file system needs to be preserved over restarts.
|
||||
*/
|
||||
if (sbi->catatonic)
|
||||
return -EACCES;
|
||||
|
||||
spin_lock(&sbi->lookup_lock);
|
||||
if (!simple_empty(dentry)) {
|
||||
spin_unlock(&sbi->lookup_lock);
|
||||
|
@ -735,6 +755,13 @@ static int autofs_dir_mkdir(struct inode *dir,
|
|||
if (!autofs_oz_mode(sbi))
|
||||
return -EACCES;
|
||||
|
||||
/* autofs_oz_mode() needs to allow path walks when the
|
||||
* autofs mount is catatonic but the state of an autofs
|
||||
* file system needs to be preserved over restarts.
|
||||
*/
|
||||
if (sbi->catatonic)
|
||||
return -EACCES;
|
||||
|
||||
pr_debug("dentry %p, creating %pd\n", dentry, dentry);
|
||||
|
||||
BUG_ON(!ino);
|
||||
|
|
118
fs/eventpoll.c
118
fs/eventpoll.c
|
@ -50,10 +50,10 @@
|
|||
*
|
||||
* 1) epmutex (mutex)
|
||||
* 2) ep->mtx (mutex)
|
||||
* 3) ep->lock (spinlock)
|
||||
* 3) ep->wq.lock (spinlock)
|
||||
*
|
||||
* The acquire order is the one listed above, from 1 to 3.
|
||||
* We need a spinlock (ep->lock) because we manipulate objects
|
||||
* We need a spinlock (ep->wq.lock) because we manipulate objects
|
||||
* from inside the poll callback, that might be triggered from
|
||||
* a wake_up() that in turn might be called from IRQ context.
|
||||
* So we can't sleep inside the poll callback and hence we need
|
||||
|
@ -85,7 +85,7 @@
|
|||
* of epoll file descriptors, we use the current recursion depth as
|
||||
* the lockdep subkey.
|
||||
* It is possible to drop the "ep->mtx" and to use the global
|
||||
* mutex "epmutex" (together with "ep->lock") to have it working,
|
||||
* mutex "epmutex" (together with "ep->wq.lock") to have it working,
|
||||
* but having "ep->mtx" will make the interface more scalable.
|
||||
* Events that require holding "epmutex" are very rare, while for
|
||||
* normal operations the epoll private "ep->mtx" will guarantee
|
||||
|
@ -182,11 +182,10 @@ struct epitem {
|
|||
* This structure is stored inside the "private_data" member of the file
|
||||
* structure and represents the main data structure for the eventpoll
|
||||
* interface.
|
||||
*
|
||||
* Access to it is protected by the lock inside wq.
|
||||
*/
|
||||
struct eventpoll {
|
||||
/* Protect the access to this structure */
|
||||
spinlock_t lock;
|
||||
|
||||
/*
|
||||
* This mutex is used to ensure that files are not removed
|
||||
* while epoll is using them. This is held during the event
|
||||
|
@ -210,7 +209,7 @@ struct eventpoll {
|
|||
/*
|
||||
* This is a single linked list that chains all the "struct epitem" that
|
||||
* happened while transferring ready events to userspace w/out
|
||||
* holding ->lock.
|
||||
* holding ->wq.lock.
|
||||
*/
|
||||
struct epitem *ovflist;
|
||||
|
||||
|
@ -337,9 +336,9 @@ static inline int ep_cmp_ffd(struct epoll_filefd *p1,
|
|||
}
|
||||
|
||||
/* Tells us if the item is currently linked */
|
||||
static inline int ep_is_linked(struct list_head *p)
|
||||
static inline int ep_is_linked(struct epitem *epi)
|
||||
{
|
||||
return !list_empty(p);
|
||||
return !list_empty(&epi->rdllink);
|
||||
}
|
||||
|
||||
static inline struct eppoll_entry *ep_pwq_from_wait(wait_queue_entry_t *p)
|
||||
|
@ -392,7 +391,6 @@ static bool ep_busy_loop_end(void *p, unsigned long start_time)
|
|||
|
||||
return ep_events_available(ep) || busy_loop_timeout(start_time);
|
||||
}
|
||||
#endif /* CONFIG_NET_RX_BUSY_POLL */
|
||||
|
||||
/*
|
||||
* Busy poll if globally on and supporting sockets found && no events,
|
||||
|
@ -402,20 +400,16 @@ static bool ep_busy_loop_end(void *p, unsigned long start_time)
|
|||
*/
|
||||
static void ep_busy_loop(struct eventpoll *ep, int nonblock)
|
||||
{
|
||||
#ifdef CONFIG_NET_RX_BUSY_POLL
|
||||
unsigned int napi_id = READ_ONCE(ep->napi_id);
|
||||
|
||||
if ((napi_id >= MIN_NAPI_ID) && net_busy_loop_on())
|
||||
napi_busy_loop(napi_id, nonblock ? NULL : ep_busy_loop_end, ep);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ep_reset_busy_poll_napi_id(struct eventpoll *ep)
|
||||
{
|
||||
#ifdef CONFIG_NET_RX_BUSY_POLL
|
||||
if (ep->napi_id)
|
||||
ep->napi_id = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -423,7 +417,6 @@ static inline void ep_reset_busy_poll_napi_id(struct eventpoll *ep)
|
|||
*/
|
||||
static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
|
||||
{
|
||||
#ifdef CONFIG_NET_RX_BUSY_POLL
|
||||
struct eventpoll *ep;
|
||||
unsigned int napi_id;
|
||||
struct socket *sock;
|
||||
|
@ -453,9 +446,24 @@ static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
|
|||
|
||||
/* record NAPI ID for use in next busy poll */
|
||||
ep->napi_id = napi_id;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void ep_busy_loop(struct eventpoll *ep, int nonblock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ep_reset_busy_poll_napi_id(struct eventpoll *ep)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_RX_BUSY_POLL */
|
||||
|
||||
/**
|
||||
* ep_call_nested - Perform a bound (possibly) nested call, by checking
|
||||
* that the recursion limit is not exceeded, and that
|
||||
|
@ -668,10 +676,11 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
|
|||
{
|
||||
__poll_t res;
|
||||
int pwake = 0;
|
||||
unsigned long flags;
|
||||
struct epitem *epi, *nepi;
|
||||
LIST_HEAD(txlist);
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
/*
|
||||
* We need to lock this because we could be hit by
|
||||
* eventpoll_release_file() and epoll_ctl().
|
||||
|
@ -688,17 +697,17 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
|
|||
* because we want the "sproc" callback to be able to do it
|
||||
* in a lockless way.
|
||||
*/
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
list_splice_init(&ep->rdllist, &txlist);
|
||||
ep->ovflist = NULL;
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
/*
|
||||
* Now call the callback function.
|
||||
*/
|
||||
res = (*sproc)(ep, &txlist, priv);
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
/*
|
||||
* During the time we spent inside the "sproc" callback, some
|
||||
* other events might have been queued by the poll callback.
|
||||
|
@ -712,7 +721,7 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
|
|||
* queued into ->ovflist but the "txlist" might already
|
||||
* contain them, and the list_splice() below takes care of them.
|
||||
*/
|
||||
if (!ep_is_linked(&epi->rdllink)) {
|
||||
if (!ep_is_linked(epi)) {
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake(epi);
|
||||
}
|
||||
|
@ -740,7 +749,7 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
|
|||
if (waitqueue_active(&ep->poll_wait))
|
||||
pwake++;
|
||||
}
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
if (!ep_locked)
|
||||
mutex_unlock(&ep->mtx);
|
||||
|
@ -764,16 +773,12 @@ static void epi_rcu_free(struct rcu_head *head)
|
|||
*/
|
||||
static int ep_remove(struct eventpoll *ep, struct epitem *epi)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct file *file = epi->ffd.file;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
/*
|
||||
* Removes poll wait queue hooks. We _have_ to do this without holding
|
||||
* the "ep->lock" otherwise a deadlock might occur. This because of the
|
||||
* sequence of the lock acquisition. Here we do "ep->lock" then the wait
|
||||
* queue head lock when unregistering the wait queue. The wakeup callback
|
||||
* will run by holding the wait queue head lock and will call our callback
|
||||
* that will try to get "ep->lock".
|
||||
* Removes poll wait queue hooks.
|
||||
*/
|
||||
ep_unregister_pollwait(ep, epi);
|
||||
|
||||
|
@ -784,10 +789,10 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)
|
|||
|
||||
rb_erase_cached(&epi->rbn, &ep->rbr);
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
if (ep_is_linked(&epi->rdllink))
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
if (ep_is_linked(epi))
|
||||
list_del_init(&epi->rdllink);
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
wakeup_source_unregister(ep_wakeup_source(epi));
|
||||
/*
|
||||
|
@ -837,7 +842,7 @@ static void ep_free(struct eventpoll *ep)
|
|||
* Walks through the whole tree by freeing each "struct epitem". At this
|
||||
* point we are sure no poll callbacks will be lingering around, and also by
|
||||
* holding "epmutex" we can be sure that no file cleanup code will hit
|
||||
* us during this operation. So we can avoid the lock on "ep->lock".
|
||||
* us during this operation. So we can avoid the lock on "ep->wq.lock".
|
||||
* We do not need to lock ep->mtx, either, we only do it to prevent
|
||||
* a lockdep warning.
|
||||
*/
|
||||
|
@ -1017,7 +1022,6 @@ static int ep_alloc(struct eventpoll **pep)
|
|||
if (unlikely(!ep))
|
||||
goto free_uid;
|
||||
|
||||
spin_lock_init(&ep->lock);
|
||||
mutex_init(&ep->mtx);
|
||||
init_waitqueue_head(&ep->wq);
|
||||
init_waitqueue_head(&ep->poll_wait);
|
||||
|
@ -1122,7 +1126,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
__poll_t pollflags = key_to_poll(key);
|
||||
int ewake = 0;
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irqsave(&ep->wq.lock, flags);
|
||||
|
||||
ep_set_busy_poll_napi_id(epi);
|
||||
|
||||
|
@ -1167,7 +1171,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
}
|
||||
|
||||
/* If this file is already in the ready list we exit soon */
|
||||
if (!ep_is_linked(&epi->rdllink)) {
|
||||
if (!ep_is_linked(epi)) {
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake_rcu(epi);
|
||||
}
|
||||
|
@ -1199,7 +1203,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
pwake++;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irqrestore(&ep->wq.lock, flags);
|
||||
|
||||
/* We have to call this outside the lock */
|
||||
if (pwake)
|
||||
|
@ -1417,11 +1421,12 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
{
|
||||
int error, pwake = 0;
|
||||
__poll_t revents;
|
||||
unsigned long flags;
|
||||
long user_watches;
|
||||
struct epitem *epi;
|
||||
struct ep_pqueue epq;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
user_watches = atomic_long_read(&ep->user->epoll_watches);
|
||||
if (unlikely(user_watches >= max_user_watches))
|
||||
return -ENOSPC;
|
||||
|
@ -1484,13 +1489,13 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
goto error_remove_epi;
|
||||
|
||||
/* We have to drop the new item inside our item list to keep track of it */
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
|
||||
/* record NAPI ID of new item if present */
|
||||
ep_set_busy_poll_napi_id(epi);
|
||||
|
||||
/* If the file is already "ready" we drop it inside the ready list */
|
||||
if (revents && !ep_is_linked(&epi->rdllink)) {
|
||||
if (revents && !ep_is_linked(epi)) {
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake(epi);
|
||||
|
||||
|
@ -1501,7 +1506,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
pwake++;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
atomic_long_inc(&ep->user->epoll_watches);
|
||||
|
||||
|
@ -1527,10 +1532,10 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
* list, since that is used/cleaned only inside a section bound by "mtx".
|
||||
* And ep_insert() is called with "mtx" held.
|
||||
*/
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
if (ep_is_linked(&epi->rdllink))
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
if (ep_is_linked(epi))
|
||||
list_del_init(&epi->rdllink);
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
wakeup_source_unregister(ep_wakeup_source(epi));
|
||||
|
||||
|
@ -1550,6 +1555,8 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
int pwake = 0;
|
||||
poll_table pt;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
init_poll_funcptr(&pt, NULL);
|
||||
|
||||
/*
|
||||
|
@ -1572,9 +1579,9 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
* 1) Flush epi changes above to other CPUs. This ensures
|
||||
* we do not miss events from ep_poll_callback if an
|
||||
* event occurs immediately after we call f_op->poll().
|
||||
* We need this because we did not take ep->lock while
|
||||
* We need this because we did not take ep->wq.lock while
|
||||
* changing epi above (but ep_poll_callback does take
|
||||
* ep->lock).
|
||||
* ep->wq.lock).
|
||||
*
|
||||
* 2) We also need to ensure we do not miss _past_ events
|
||||
* when calling f_op->poll(). This barrier also
|
||||
|
@ -1593,8 +1600,8 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
* list, push it inside.
|
||||
*/
|
||||
if (ep_item_poll(epi, &pt, 1)) {
|
||||
spin_lock_irq(&ep->lock);
|
||||
if (!ep_is_linked(&epi->rdllink)) {
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
if (!ep_is_linked(epi)) {
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake(epi);
|
||||
|
||||
|
@ -1604,7 +1611,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
if (waitqueue_active(&ep->poll_wait))
|
||||
pwake++;
|
||||
}
|
||||
spin_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
}
|
||||
|
||||
/* We have to call this outside the lock */
|
||||
|
@ -1739,11 +1746,12 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
int maxevents, long timeout)
|
||||
{
|
||||
int res = 0, eavail, timed_out = 0;
|
||||
unsigned long flags;
|
||||
u64 slack = 0;
|
||||
wait_queue_entry_t wait;
|
||||
ktime_t expires, *to = NULL;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
if (timeout > 0) {
|
||||
struct timespec64 end_time = ep_set_mstimeout(timeout);
|
||||
|
||||
|
@ -1756,7 +1764,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
* caller specified a non blocking operation.
|
||||
*/
|
||||
timed_out = 1;
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
goto check_events;
|
||||
}
|
||||
|
||||
|
@ -1765,7 +1773,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
if (!ep_events_available(ep))
|
||||
ep_busy_loop(ep, timed_out);
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
|
||||
if (!ep_events_available(ep)) {
|
||||
/*
|
||||
|
@ -1807,11 +1815,11 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
|
||||
timed_out = 1;
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irq(&ep->wq.lock);
|
||||
}
|
||||
|
||||
__remove_wait_queue(&ep->wq, &wait);
|
||||
|
@ -1821,7 +1829,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
/* Is it worth to try to dig for events ? */
|
||||
eavail = ep_events_available(ep);
|
||||
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irq(&ep->wq.lock);
|
||||
|
||||
/*
|
||||
* Try to transfer events to user space. In case we get 0 events and
|
||||
|
|
|
@ -225,7 +225,8 @@ static inline void cache_init(struct fat_cache_id *cid, int fclus, int dclus)
|
|||
int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
const int limit = sb->s_maxbytes >> MSDOS_SB(sb)->cluster_bits;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
const int limit = sb->s_maxbytes >> sbi->cluster_bits;
|
||||
struct fat_entry fatent;
|
||||
struct fat_cache_id cid;
|
||||
int nr;
|
||||
|
@ -234,6 +235,12 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
|
|||
|
||||
*fclus = 0;
|
||||
*dclus = MSDOS_I(inode)->i_start;
|
||||
if (!fat_valid_entry(sbi, *dclus)) {
|
||||
fat_fs_error_ratelimit(sb,
|
||||
"%s: invalid start cluster (i_pos %lld, start %08x)",
|
||||
__func__, MSDOS_I(inode)->i_pos, *dclus);
|
||||
return -EIO;
|
||||
}
|
||||
if (cluster == 0)
|
||||
return 0;
|
||||
|
||||
|
@ -250,9 +257,8 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
|
|||
/* prevent the infinite loop of cluster chain */
|
||||
if (*fclus > limit) {
|
||||
fat_fs_error_ratelimit(sb,
|
||||
"%s: detected the cluster chain loop"
|
||||
" (i_pos %lld)", __func__,
|
||||
MSDOS_I(inode)->i_pos);
|
||||
"%s: detected the cluster chain loop (i_pos %lld)",
|
||||
__func__, MSDOS_I(inode)->i_pos);
|
||||
nr = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
@ -262,9 +268,8 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
|
|||
goto out;
|
||||
else if (nr == FAT_ENT_FREE) {
|
||||
fat_fs_error_ratelimit(sb,
|
||||
"%s: invalid cluster chain (i_pos %lld)",
|
||||
__func__,
|
||||
MSDOS_I(inode)->i_pos);
|
||||
"%s: invalid cluster chain (i_pos %lld)",
|
||||
__func__, MSDOS_I(inode)->i_pos);
|
||||
nr = -EIO;
|
||||
goto out;
|
||||
} else if (nr == FAT_ENT_EOF) {
|
||||
|
|
|
@ -1130,7 +1130,7 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
|
|||
return err;
|
||||
}
|
||||
|
||||
int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
|
||||
int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
|
|
12
fs/fat/fat.h
12
fs/fat/fat.h
|
@ -304,7 +304,7 @@ extern int fat_scan_logstart(struct inode *dir, int i_logstart,
|
|||
struct fat_slot_info *sinfo);
|
||||
extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
|
||||
struct msdos_dir_entry **de);
|
||||
extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
|
||||
extern int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts);
|
||||
extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
|
||||
struct fat_slot_info *sinfo);
|
||||
extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo);
|
||||
|
@ -348,6 +348,11 @@ static inline void fatent_brelse(struct fat_entry *fatent)
|
|||
fatent->fat_inode = NULL;
|
||||
}
|
||||
|
||||
static inline bool fat_valid_entry(struct msdos_sb_info *sbi, int entry)
|
||||
{
|
||||
return FAT_START_ENT <= entry && entry < sbi->max_cluster;
|
||||
}
|
||||
|
||||
extern void fat_ent_access_init(struct super_block *sb);
|
||||
extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent,
|
||||
int entry);
|
||||
|
@ -357,6 +362,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
|
|||
int nr_cluster);
|
||||
extern int fat_free_clusters(struct inode *inode, int cluster);
|
||||
extern int fat_count_free_clusters(struct super_block *sb);
|
||||
extern int fat_trim_fs(struct inode *inode, struct fstrim_range *range);
|
||||
|
||||
/* fat/file.c */
|
||||
extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
|
||||
|
@ -406,9 +412,9 @@ void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...);
|
|||
} while (0)
|
||||
extern int fat_clusters_flush(struct super_block *sb);
|
||||
extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
|
||||
extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
|
||||
__le16 __time, __le16 __date, u8 time_cs);
|
||||
extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
|
||||
__le16 *time, __le16 *date, u8 *time_cs);
|
||||
extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
|
||||
|
||||
|
|
108
fs/fat/fatent.c
108
fs/fat/fatent.c
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include "fat.h"
|
||||
|
||||
struct fatent_operations {
|
||||
|
@ -23,7 +24,7 @@ static void fat12_ent_blocknr(struct super_block *sb, int entry,
|
|||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
int bytes = entry + (entry >> 1);
|
||||
WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
|
||||
WARN_ON(!fat_valid_entry(sbi, entry));
|
||||
*offset = bytes & (sb->s_blocksize - 1);
|
||||
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
||||
}
|
||||
|
@ -33,7 +34,7 @@ static void fat_ent_blocknr(struct super_block *sb, int entry,
|
|||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
int bytes = (entry << sbi->fatent_shift);
|
||||
WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
|
||||
WARN_ON(!fat_valid_entry(sbi, entry));
|
||||
*offset = bytes & (sb->s_blocksize - 1);
|
||||
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
||||
}
|
||||
|
@ -353,7 +354,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
|
|||
int err, offset;
|
||||
sector_t blocknr;
|
||||
|
||||
if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
|
||||
if (!fat_valid_entry(sbi, entry)) {
|
||||
fatent_brelse(fatent);
|
||||
fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
|
||||
return -EIO;
|
||||
|
@ -690,3 +691,104 @@ int fat_count_free_clusters(struct super_block *sb)
|
|||
unlock_fat(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fat_trim_clusters(struct super_block *sb, u32 clus, u32 nr_clus)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
return sb_issue_discard(sb, fat_clus_to_blknr(sbi, clus),
|
||||
nr_clus * sbi->sec_per_clus, GFP_NOFS, 0);
|
||||
}
|
||||
|
||||
int fat_trim_fs(struct inode *inode, struct fstrim_range *range)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
const struct fatent_operations *ops = sbi->fatent_ops;
|
||||
struct fat_entry fatent;
|
||||
u64 ent_start, ent_end, minlen, trimmed = 0;
|
||||
u32 free = 0;
|
||||
unsigned long reada_blocks, reada_mask, cur_block = 0;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* FAT data is organized as clusters, trim at the granulary of cluster.
|
||||
*
|
||||
* fstrim_range is in byte, convert vaules to cluster index.
|
||||
* Treat sectors before data region as all used, not to trim them.
|
||||
*/
|
||||
ent_start = max_t(u64, range->start>>sbi->cluster_bits, FAT_START_ENT);
|
||||
ent_end = ent_start + (range->len >> sbi->cluster_bits) - 1;
|
||||
minlen = range->minlen >> sbi->cluster_bits;
|
||||
|
||||
if (ent_start >= sbi->max_cluster || range->len < sbi->cluster_size)
|
||||
return -EINVAL;
|
||||
if (ent_end >= sbi->max_cluster)
|
||||
ent_end = sbi->max_cluster - 1;
|
||||
|
||||
reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
|
||||
reada_mask = reada_blocks - 1;
|
||||
|
||||
fatent_init(&fatent);
|
||||
lock_fat(sbi);
|
||||
fatent_set_entry(&fatent, ent_start);
|
||||
while (fatent.entry <= ent_end) {
|
||||
/* readahead of fat blocks */
|
||||
if ((cur_block & reada_mask) == 0) {
|
||||
unsigned long rest = sbi->fat_length - cur_block;
|
||||
fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
|
||||
}
|
||||
cur_block++;
|
||||
|
||||
err = fat_ent_read_block(sb, &fatent);
|
||||
if (err)
|
||||
goto error;
|
||||
do {
|
||||
if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
|
||||
free++;
|
||||
} else if (free) {
|
||||
if (free >= minlen) {
|
||||
u32 clus = fatent.entry - free;
|
||||
|
||||
err = fat_trim_clusters(sb, clus, free);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
goto error;
|
||||
if (!err)
|
||||
trimmed += free;
|
||||
err = 0;
|
||||
}
|
||||
free = 0;
|
||||
}
|
||||
} while (fat_ent_next(sbi, &fatent) && fatent.entry <= ent_end);
|
||||
|
||||
if (fatal_signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (need_resched()) {
|
||||
fatent_brelse(&fatent);
|
||||
unlock_fat(sbi);
|
||||
cond_resched();
|
||||
lock_fat(sbi);
|
||||
}
|
||||
}
|
||||
/* handle scenario when tail entries are all free */
|
||||
if (free && free >= minlen) {
|
||||
u32 clus = fatent.entry - free;
|
||||
|
||||
err = fat_trim_clusters(sb, clus, free);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
goto error;
|
||||
if (!err)
|
||||
trimmed += free;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
error:
|
||||
fatent_brelse(&fatent);
|
||||
unlock_fat(sbi);
|
||||
|
||||
range->len = trimmed << sbi->cluster_bits;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,37 @@ static int fat_ioctl_get_volume_id(struct inode *inode, u32 __user *user_attr)
|
|||
return put_user(sbi->vol_id, user_attr);
|
||||
}
|
||||
|
||||
static int fat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct fstrim_range __user *user_range;
|
||||
struct fstrim_range range;
|
||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||
int err;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!blk_queue_discard(q))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
user_range = (struct fstrim_range __user *)arg;
|
||||
if (copy_from_user(&range, user_range, sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
range.minlen = max_t(unsigned int, range.minlen,
|
||||
q->limits.discard_granularity);
|
||||
|
||||
err = fat_trim_fs(inode, &range);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (copy_to_user(user_range, &range, sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
@ -133,6 +164,8 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
return fat_ioctl_set_attributes(filp, user_attr);
|
||||
case FAT_IOCTL_GET_VOLUME_ID:
|
||||
return fat_ioctl_get_volume_id(inode, user_attr);
|
||||
case FITRIM:
|
||||
return fat_ioctl_fitrim(inode, arg);
|
||||
default:
|
||||
return -ENOTTY; /* Inappropriate ioctl for device */
|
||||
}
|
||||
|
|
|
@ -508,7 +508,6 @@ static int fat_validate_dir(struct inode *dir)
|
|||
/* doesn't deal with root inode */
|
||||
int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
int error;
|
||||
|
||||
|
@ -559,14 +558,11 @@ int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
|
|||
inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
|
||||
& ~((loff_t)sbi->cluster_size - 1)) >> 9;
|
||||
|
||||
fat_time_fat2unix(sbi, &ts, de->time, de->date, 0);
|
||||
inode->i_mtime = timespec_to_timespec64(ts);
|
||||
fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
|
||||
if (sbi->options.isvfat) {
|
||||
fat_time_fat2unix(sbi, &ts, de->ctime,
|
||||
fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime,
|
||||
de->cdate, de->ctime_cs);
|
||||
inode->i_ctime = timespec_to_timespec64(ts);
|
||||
fat_time_fat2unix(sbi, &ts, 0, de->adate, 0);
|
||||
inode->i_atime = timespec_to_timespec64(ts);
|
||||
fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
|
||||
} else
|
||||
inode->i_ctime = inode->i_atime = inode->i_mtime;
|
||||
|
||||
|
@ -843,7 +839,6 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
|
||||
static int __fat_write_inode(struct inode *inode, int wait)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct buffer_head *bh;
|
||||
|
@ -881,16 +876,13 @@ static int __fat_write_inode(struct inode *inode, int wait)
|
|||
raw_entry->size = cpu_to_le32(inode->i_size);
|
||||
raw_entry->attr = fat_make_attrs(inode);
|
||||
fat_set_start(raw_entry, MSDOS_I(inode)->i_logstart);
|
||||
ts = timespec64_to_timespec(inode->i_mtime);
|
||||
fat_time_unix2fat(sbi, &ts, &raw_entry->time,
|
||||
fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
|
||||
&raw_entry->date, NULL);
|
||||
if (sbi->options.isvfat) {
|
||||
__le16 atime;
|
||||
ts = timespec64_to_timespec(inode->i_ctime);
|
||||
fat_time_unix2fat(sbi, &ts, &raw_entry->ctime,
|
||||
fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime,
|
||||
&raw_entry->cdate, &raw_entry->ctime_cs);
|
||||
ts = timespec64_to_timespec(inode->i_atime);
|
||||
fat_time_unix2fat(sbi, &ts, &atime,
|
||||
fat_time_unix2fat(sbi, &inode->i_atime, &atime,
|
||||
&raw_entry->adate, NULL);
|
||||
}
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
|
|
|
@ -180,17 +180,18 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
|
|||
#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
|
||||
|
||||
/* Linear day numbers of the respective 1sts in non-leap years. */
|
||||
static time_t days_in_year[] = {
|
||||
static long days_in_year[] = {
|
||||
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
|
||||
0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
|
||||
};
|
||||
|
||||
/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
|
||||
void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
|
||||
__le16 __time, __le16 __date, u8 time_cs)
|
||||
{
|
||||
u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date);
|
||||
time_t second, day, leap_day, month, year;
|
||||
time64_t second;
|
||||
long day, leap_day, month, year;
|
||||
|
||||
year = date >> 9;
|
||||
month = max(1, (date >> 5) & 0xf);
|
||||
|
@ -205,7 +206,7 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
|||
second = (time & 0x1f) << 1;
|
||||
second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
|
||||
second += (time >> 11) * SECS_PER_HOUR;
|
||||
second += (year * 365 + leap_day
|
||||
second += (time64_t)(year * 365 + leap_day
|
||||
+ days_in_year[month] + day
|
||||
+ DAYS_DELTA) * SECS_PER_DAY;
|
||||
|
||||
|
@ -224,11 +225,11 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
|||
}
|
||||
|
||||
/* Convert linear UNIX date to a FAT time/date pair. */
|
||||
void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
|
||||
__le16 *time, __le16 *date, u8 *time_cs)
|
||||
{
|
||||
struct tm tm;
|
||||
time_to_tm(ts->tv_sec,
|
||||
time64_to_tm(ts->tv_sec,
|
||||
(sbi->options.tz_set ? sbi->options.time_offset :
|
||||
-sys_tz.tz_minuteswest) * SECS_PER_MIN, &tm);
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
|
|||
/***** Creates a directory entry (name is already formatted). */
|
||||
static int msdos_add_entry(struct inode *dir, const unsigned char *name,
|
||||
int is_dir, int is_hid, int cluster,
|
||||
struct timespec *ts, struct fat_slot_info *sinfo)
|
||||
struct timespec64 *ts, struct fat_slot_info *sinfo)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
|
||||
struct msdos_dir_entry de;
|
||||
|
@ -250,7 +250,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
dir->i_ctime = dir->i_mtime = timespec_to_timespec64(*ts);
|
||||
dir->i_ctime = dir->i_mtime = *ts;
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
|
@ -267,7 +267,6 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
struct inode *inode = NULL;
|
||||
struct fat_slot_info sinfo;
|
||||
struct timespec64 ts;
|
||||
struct timespec t;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
int err, is_hid;
|
||||
|
||||
|
@ -286,8 +285,7 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
}
|
||||
|
||||
ts = current_time(dir);
|
||||
t = timespec64_to_timespec(ts);
|
||||
err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &t, &sinfo);
|
||||
err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
|
||||
|
@ -347,7 +345,6 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
struct inode *inode;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
struct timespec64 ts;
|
||||
struct timespec t;
|
||||
int err, is_hid, cluster;
|
||||
|
||||
mutex_lock(&MSDOS_SB(sb)->s_lock);
|
||||
|
@ -365,13 +362,12 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
}
|
||||
|
||||
ts = current_time(dir);
|
||||
t = timespec64_to_timespec(ts);
|
||||
cluster = fat_alloc_new_dir(dir, &t);
|
||||
cluster = fat_alloc_new_dir(dir, &ts);
|
||||
if (cluster < 0) {
|
||||
err = cluster;
|
||||
goto out;
|
||||
}
|
||||
err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &t, &sinfo);
|
||||
err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out_free;
|
||||
inc_nlink(dir);
|
||||
|
@ -503,9 +499,8 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
|||
new_i_pos = MSDOS_I(new_inode)->i_pos;
|
||||
fat_detach(new_inode);
|
||||
} else {
|
||||
struct timespec t = timespec64_to_timespec(ts);
|
||||
err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
|
||||
&t, &sinfo);
|
||||
&ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
new_i_pos = sinfo.i_pos;
|
||||
|
|
|
@ -577,7 +577,7 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
|
|||
|
||||
static int vfat_build_slots(struct inode *dir, const unsigned char *name,
|
||||
int len, int is_dir, int cluster,
|
||||
struct timespec *ts,
|
||||
struct timespec64 *ts,
|
||||
struct msdos_dir_slot *slots, int *nr_slots)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
|
||||
|
@ -653,7 +653,7 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name,
|
|||
}
|
||||
|
||||
static int vfat_add_entry(struct inode *dir, const struct qstr *qname,
|
||||
int is_dir, int cluster, struct timespec *ts,
|
||||
int is_dir, int cluster, struct timespec64 *ts,
|
||||
struct fat_slot_info *sinfo)
|
||||
{
|
||||
struct msdos_dir_slot *slots;
|
||||
|
@ -678,7 +678,7 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname,
|
|||
goto cleanup;
|
||||
|
||||
/* update timestamp */
|
||||
dir->i_ctime = dir->i_mtime = dir->i_atime = timespec_to_timespec64(*ts);
|
||||
dir->i_ctime = dir->i_mtime = dir->i_atime = *ts;
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
|
@ -762,14 +762,12 @@ static int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
struct inode *inode;
|
||||
struct fat_slot_info sinfo;
|
||||
struct timespec64 ts;
|
||||
struct timespec t;
|
||||
int err;
|
||||
|
||||
mutex_lock(&MSDOS_SB(sb)->s_lock);
|
||||
|
||||
ts = current_time(dir);
|
||||
t = timespec64_to_timespec(ts);
|
||||
err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &t, &sinfo);
|
||||
err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
inode_inc_iversion(dir);
|
||||
|
@ -853,19 +851,17 @@ static int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
struct inode *inode;
|
||||
struct fat_slot_info sinfo;
|
||||
struct timespec64 ts;
|
||||
struct timespec t;
|
||||
int err, cluster;
|
||||
|
||||
mutex_lock(&MSDOS_SB(sb)->s_lock);
|
||||
|
||||
ts = current_time(dir);
|
||||
t = timespec64_to_timespec(ts);
|
||||
cluster = fat_alloc_new_dir(dir, &t);
|
||||
cluster = fat_alloc_new_dir(dir, &ts);
|
||||
if (cluster < 0) {
|
||||
err = cluster;
|
||||
goto out;
|
||||
}
|
||||
err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &t, &sinfo);
|
||||
err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out_free;
|
||||
inode_inc_iversion(dir);
|
||||
|
@ -904,7 +900,6 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
struct inode *old_inode, *new_inode;
|
||||
struct fat_slot_info old_sinfo, sinfo;
|
||||
struct timespec64 ts;
|
||||
struct timespec t;
|
||||
loff_t new_i_pos;
|
||||
int err, is_dir, update_dotdot, corrupt = 0;
|
||||
struct super_block *sb = old_dir->i_sb;
|
||||
|
@ -939,9 +934,8 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
new_i_pos = MSDOS_I(new_inode)->i_pos;
|
||||
fat_detach(new_inode);
|
||||
} else {
|
||||
t = timespec64_to_timespec(ts);
|
||||
err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0,
|
||||
&t, &sinfo);
|
||||
&ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
new_i_pos = sinfo.i_pos;
|
||||
|
|
|
@ -11,18 +11,3 @@ config HFSPLUS_FS
|
|||
MacOS 8. It includes all Mac specific filesystem data such as
|
||||
data forks and creator codes, but it also has several UNIX
|
||||
style features such as file ownership and permissions.
|
||||
|
||||
config HFSPLUS_FS_POSIX_ACL
|
||||
bool "HFS+ POSIX Access Control Lists"
|
||||
depends on HFSPLUS_FS
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
POSIX Access Control Lists (ACLs) support permissions for users and
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
It needs to understand that POSIX ACLs are treated only under
|
||||
Linux. POSIX ACLs doesn't mean something under Mac OS X.
|
||||
Mac OS X beginning with version 10.4 ("Tiger") support NFSv4 ACLs,
|
||||
which are part of the NFSv4 standard.
|
||||
|
||||
If you don't know what Access Control Lists are, say N
|
||||
|
|
|
@ -8,5 +8,3 @@ obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
|
|||
hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
|
||||
bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \
|
||||
attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o
|
||||
|
||||
hfsplus-$(CONFIG_HFSPLUS_FS_POSIX_ACL) += posix_acl.o
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* linux/fs/hfsplus/acl.h
|
||||
*
|
||||
* Vyacheslav Dubeyko <slava@dubeyko.com>
|
||||
*
|
||||
* Handler for Posix Access Control Lists (ACLs) support.
|
||||
*/
|
||||
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
|
||||
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
|
||||
|
||||
/* posix_acl.c */
|
||||
struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type);
|
||||
int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
|
||||
int type);
|
||||
extern int hfsplus_init_posix_acl(struct inode *, struct inode *);
|
||||
|
||||
#else /* CONFIG_HFSPLUS_FS_POSIX_ACL */
|
||||
#define hfsplus_get_posix_acl NULL
|
||||
#define hfsplus_set_posix_acl NULL
|
||||
|
||||
static inline int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HFSPLUS_FS_POSIX_ACL */
|
|
@ -18,7 +18,6 @@
|
|||
#include "hfsplus_fs.h"
|
||||
#include "hfsplus_raw.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static inline void hfsplus_instantiate(struct dentry *dentry,
|
||||
struct inode *inode, u32 cnid)
|
||||
|
@ -455,7 +454,7 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
|
|||
if (res)
|
||||
goto out_err;
|
||||
|
||||
res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
|
||||
res = hfsplus_init_security(inode, dir, &dentry->d_name);
|
||||
if (res == -EOPNOTSUPP)
|
||||
res = 0; /* Operation is not supported. */
|
||||
else if (res) {
|
||||
|
@ -496,7 +495,7 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
|
|||
if (res)
|
||||
goto failed_mknod;
|
||||
|
||||
res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
|
||||
res = hfsplus_init_security(inode, dir, &dentry->d_name);
|
||||
if (res == -EOPNOTSUPP)
|
||||
res = 0; /* Operation is not supported. */
|
||||
else if (res) {
|
||||
|
@ -567,10 +566,6 @@ const struct inode_operations hfsplus_dir_inode_operations = {
|
|||
.mknod = hfsplus_mknod,
|
||||
.rename = hfsplus_rename,
|
||||
.listxattr = hfsplus_listxattr,
|
||||
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
|
||||
.get_acl = hfsplus_get_posix_acl,
|
||||
.set_acl = hfsplus_set_posix_acl,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct file_operations hfsplus_dir_operations = {
|
||||
|
|
|
@ -336,6 +336,9 @@ static int hfsplus_free_extents(struct super_block *sb,
|
|||
int i;
|
||||
int err = 0;
|
||||
|
||||
/* Mapping the allocation file may lock the extent tree */
|
||||
WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
|
||||
|
||||
hfsplus_dump_extent(extent);
|
||||
for (i = 0; i < 8; extent++, i++) {
|
||||
count = be32_to_cpu(extent->block_count);
|
||||
|
@ -415,11 +418,13 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
|
|||
if (res)
|
||||
break;
|
||||
start = be32_to_cpu(fd.key->ext.start_block);
|
||||
hfsplus_free_extents(sb, ext_entry,
|
||||
total_blocks - start,
|
||||
total_blocks);
|
||||
hfs_brec_remove(&fd);
|
||||
|
||||
mutex_unlock(&fd.tree->tree_lock);
|
||||
hfsplus_free_extents(sb, ext_entry, total_blocks - start,
|
||||
total_blocks);
|
||||
total_blocks = start;
|
||||
mutex_lock(&fd.tree->tree_lock);
|
||||
} while (total_blocks > blocks);
|
||||
hfs_find_exit(&fd);
|
||||
|
||||
|
@ -576,15 +581,20 @@ void hfsplus_file_truncate(struct inode *inode)
|
|||
}
|
||||
while (1) {
|
||||
if (alloc_cnt == hip->first_blocks) {
|
||||
mutex_unlock(&fd.tree->tree_lock);
|
||||
hfsplus_free_extents(sb, hip->first_extents,
|
||||
alloc_cnt, alloc_cnt - blk_cnt);
|
||||
hfsplus_dump_extent(hip->first_extents);
|
||||
hip->first_blocks = blk_cnt;
|
||||
mutex_lock(&fd.tree->tree_lock);
|
||||
break;
|
||||
}
|
||||
res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
|
||||
if (res)
|
||||
break;
|
||||
hfs_brec_remove(&fd);
|
||||
|
||||
mutex_unlock(&fd.tree->tree_lock);
|
||||
start = hip->cached_start;
|
||||
hfsplus_free_extents(sb, hip->cached_extents,
|
||||
alloc_cnt - start, alloc_cnt - blk_cnt);
|
||||
|
@ -596,7 +606,7 @@ void hfsplus_file_truncate(struct inode *inode)
|
|||
alloc_cnt = start;
|
||||
hip->cached_start = hip->cached_blocks = 0;
|
||||
hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
|
||||
hfs_brec_remove(&fd);
|
||||
mutex_lock(&fd.tree->tree_lock);
|
||||
}
|
||||
hfs_find_exit(&fd);
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#define DBG_EXTENT 0x00000020
|
||||
#define DBG_BITMAP 0x00000040
|
||||
#define DBG_ATTR_MOD 0x00000080
|
||||
#define DBG_ACL_MOD 0x00000100
|
||||
|
||||
#if 0
|
||||
#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "hfsplus_fs.h"
|
||||
#include "hfsplus_raw.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static int hfsplus_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
|
@ -267,12 +266,6 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
setattr_copy(inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
if (attr->ia_valid & ATTR_MODE) {
|
||||
error = posix_acl_chmod(inode, inode->i_mode);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -336,10 +329,6 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
|
|||
static const struct inode_operations hfsplus_file_inode_operations = {
|
||||
.setattr = hfsplus_setattr,
|
||||
.listxattr = hfsplus_listxattr,
|
||||
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
|
||||
.get_acl = hfsplus_get_posix_acl,
|
||||
.set_acl = hfsplus_set_posix_acl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct file_operations hfsplus_file_operations = {
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/fs/hfsplus/posix_acl.c
|
||||
*
|
||||
* Vyacheslav Dubeyko <slava@dubeyko.com>
|
||||
*
|
||||
* Handler for Posix Access Control Lists (ACLs) support.
|
||||
*/
|
||||
|
||||
#include "hfsplus_fs.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
char *xattr_name;
|
||||
char *value = NULL;
|
||||
ssize_t size;
|
||||
|
||||
hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = __hfsplus_getxattr(inode, xattr_name, NULL, 0);
|
||||
|
||||
if (size > 0) {
|
||||
value = (char *)hfsplus_alloc_attr_entry();
|
||||
if (unlikely(!value))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
size = __hfsplus_getxattr(inode, xattr_name, value, size);
|
||||
}
|
||||
|
||||
if (size > 0)
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
else if (size == -ENODATA)
|
||||
acl = NULL;
|
||||
else
|
||||
acl = ERR_PTR(size);
|
||||
|
||||
hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
|
||||
|
||||
return acl;
|
||||
}
|
||||
|
||||
static int __hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
|
||||
int type)
|
||||
{
|
||||
int err;
|
||||
char *xattr_name;
|
||||
size_t size = 0;
|
||||
char *value = NULL;
|
||||
|
||||
hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
break;
|
||||
|
||||
case ACL_TYPE_DEFAULT:
|
||||
xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return acl ? -EACCES : 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (acl) {
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
if (unlikely(size > HFSPLUS_MAX_INLINE_DATA_SIZE))
|
||||
return -ENOMEM;
|
||||
value = (char *)hfsplus_alloc_attr_entry();
|
||||
if (unlikely(!value))
|
||||
return -ENOMEM;
|
||||
err = posix_acl_to_xattr(&init_user_ns, acl, value, size);
|
||||
if (unlikely(err < 0))
|
||||
goto end_set_acl;
|
||||
}
|
||||
|
||||
err = __hfsplus_setxattr(inode, xattr_name, value, size, 0);
|
||||
|
||||
end_set_acl:
|
||||
hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
|
||||
|
||||
if (!err)
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (type == ACL_TYPE_ACCESS && acl) {
|
||||
err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return __hfsplus_set_posix_acl(inode, acl, type);
|
||||
}
|
||||
|
||||
int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
int err = 0;
|
||||
struct posix_acl *default_acl, *acl;
|
||||
|
||||
hfs_dbg(ACL_MOD,
|
||||
"[%s]: ino %lu, dir->ino %lu\n",
|
||||
__func__, inode->i_ino, dir->i_ino);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (default_acl) {
|
||||
err = __hfsplus_set_posix_acl(inode, default_acl,
|
||||
ACL_TYPE_DEFAULT);
|
||||
posix_acl_release(default_acl);
|
||||
}
|
||||
|
||||
if (acl) {
|
||||
if (!err)
|
||||
err = __hfsplus_set_posix_acl(inode, acl,
|
||||
ACL_TYPE_ACCESS);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
return err;
|
||||
}
|
|
@ -524,8 +524,10 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|||
goto out_put_root;
|
||||
if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
|
||||
hfs_find_exit(&fd);
|
||||
if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
|
||||
if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) {
|
||||
err = -EINVAL;
|
||||
goto out_put_root;
|
||||
}
|
||||
inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
|
@ -562,8 +564,8 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|||
goto out_put_hidden_dir;
|
||||
}
|
||||
|
||||
err = hfsplus_init_inode_security(sbi->hidden_dir,
|
||||
root, &str);
|
||||
err = hfsplus_init_security(sbi->hidden_dir,
|
||||
root, &str);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0; /* Operation is not supported. */
|
||||
else if (err) {
|
||||
|
|
|
@ -272,8 +272,8 @@ static inline int asc2unichar(struct super_block *sb, const char *astr, int len,
|
|||
return size;
|
||||
}
|
||||
|
||||
/* Decomposes a single unicode character. */
|
||||
static inline u16 *decompose_unichar(wchar_t uc, int *size)
|
||||
/* Decomposes a non-Hangul unicode character. */
|
||||
static u16 *hfsplus_decompose_nonhangul(wchar_t uc, int *size)
|
||||
{
|
||||
int off;
|
||||
|
||||
|
@ -296,6 +296,51 @@ static inline u16 *decompose_unichar(wchar_t uc, int *size)
|
|||
return hfsplus_decompose_table + (off / 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to decompose a unicode character as Hangul. Return 0 if @uc is not
|
||||
* precomposed Hangul, otherwise return the length of the decomposition.
|
||||
*
|
||||
* This function was adapted from sample code from the Unicode Standard
|
||||
* Annex #15: Unicode Normalization Forms, version 3.2.0.
|
||||
*
|
||||
* Copyright (C) 1991-2018 Unicode, Inc. All rights reserved. Distributed
|
||||
* under the Terms of Use in http://www.unicode.org/copyright.html.
|
||||
*/
|
||||
static int hfsplus_try_decompose_hangul(wchar_t uc, u16 *result)
|
||||
{
|
||||
int index;
|
||||
int l, v, t;
|
||||
|
||||
index = uc - Hangul_SBase;
|
||||
if (index < 0 || index >= Hangul_SCount)
|
||||
return 0;
|
||||
|
||||
l = Hangul_LBase + index / Hangul_NCount;
|
||||
v = Hangul_VBase + (index % Hangul_NCount) / Hangul_TCount;
|
||||
t = Hangul_TBase + index % Hangul_TCount;
|
||||
|
||||
result[0] = l;
|
||||
result[1] = v;
|
||||
if (t != Hangul_TBase) {
|
||||
result[2] = t;
|
||||
return 3;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Decomposes a single unicode character. */
|
||||
static u16 *decompose_unichar(wchar_t uc, int *size, u16 *hangul_buffer)
|
||||
{
|
||||
u16 *result;
|
||||
|
||||
/* Hangul is handled separately */
|
||||
result = hangul_buffer;
|
||||
*size = hfsplus_try_decompose_hangul(uc, result);
|
||||
if (*size == 0)
|
||||
result = hfsplus_decompose_nonhangul(uc, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
int hfsplus_asc2uni(struct super_block *sb,
|
||||
struct hfsplus_unistr *ustr, int max_unistr_len,
|
||||
const char *astr, int len)
|
||||
|
@ -303,13 +348,14 @@ int hfsplus_asc2uni(struct super_block *sb,
|
|||
int size, dsize, decompose;
|
||||
u16 *dstr, outlen = 0;
|
||||
wchar_t c;
|
||||
u16 dhangul[3];
|
||||
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
while (outlen < max_unistr_len && len > 0) {
|
||||
size = asc2unichar(sb, astr, len, &c);
|
||||
|
||||
if (decompose)
|
||||
dstr = decompose_unichar(c, &dsize);
|
||||
dstr = decompose_unichar(c, &dsize, dhangul);
|
||||
else
|
||||
dstr = NULL;
|
||||
if (dstr) {
|
||||
|
@ -344,6 +390,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
|
|||
unsigned long hash;
|
||||
wchar_t c;
|
||||
u16 c2;
|
||||
u16 dhangul[3];
|
||||
|
||||
casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
|
@ -357,7 +404,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
|
|||
len -= size;
|
||||
|
||||
if (decompose)
|
||||
dstr = decompose_unichar(c, &dsize);
|
||||
dstr = decompose_unichar(c, &dsize, dhangul);
|
||||
else
|
||||
dstr = NULL;
|
||||
if (dstr) {
|
||||
|
@ -396,6 +443,7 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
|
|||
const char *astr1, *astr2;
|
||||
u16 c1, c2;
|
||||
wchar_t c;
|
||||
u16 dhangul_1[3], dhangul_2[3];
|
||||
|
||||
casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
|
@ -413,7 +461,8 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
|
|||
len1 -= size;
|
||||
|
||||
if (decompose)
|
||||
dstr1 = decompose_unichar(c, &dsize1);
|
||||
dstr1 = decompose_unichar(c, &dsize1,
|
||||
dhangul_1);
|
||||
if (!decompose || !dstr1) {
|
||||
c1 = c;
|
||||
dstr1 = &c1;
|
||||
|
@ -427,7 +476,8 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
|
|||
len2 -= size;
|
||||
|
||||
if (decompose)
|
||||
dstr2 = decompose_unichar(c, &dsize2);
|
||||
dstr2 = decompose_unichar(c, &dsize2,
|
||||
dhangul_2);
|
||||
if (!decompose || !dstr2) {
|
||||
c2 = c;
|
||||
dstr2 = &c2;
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
*/
|
||||
|
||||
#include "hfsplus_fs.h"
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/nls.h>
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static int hfsplus_removexattr(struct inode *inode, const char *name);
|
||||
|
||||
|
@ -19,10 +17,6 @@ const struct xattr_handler *hfsplus_xattr_handlers[] = {
|
|||
&hfsplus_xattr_osx_handler,
|
||||
&hfsplus_xattr_user_handler,
|
||||
&hfsplus_xattr_trusted_handler,
|
||||
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
|
||||
&posix_acl_access_xattr_handler,
|
||||
&posix_acl_default_xattr_handler,
|
||||
#endif
|
||||
&hfsplus_xattr_security_handler,
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -38,7 +38,4 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
|||
int hfsplus_init_security(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr);
|
||||
|
||||
int hfsplus_init_inode_security(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "hfsplus_fs.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static int hfsplus_security_getxattr(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
|
@ -72,18 +71,6 @@ int hfsplus_init_security(struct inode *inode, struct inode *dir,
|
|||
&hfsplus_initxattrs, NULL);
|
||||
}
|
||||
|
||||
int hfsplus_init_inode_security(struct inode *inode,
|
||||
struct inode *dir,
|
||||
const struct qstr *qstr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hfsplus_init_posix_acl(inode, dir);
|
||||
if (!err)
|
||||
err = hfsplus_init_security(inode, dir, qstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct xattr_handler hfsplus_xattr_security_handler = {
|
||||
.prefix = XATTR_SECURITY_PREFIX,
|
||||
.get = hfsplus_security_getxattr,
|
||||
|
|
|
@ -410,7 +410,6 @@ static void remove_inode_hugepages(struct inode *inode, loff_t lstart,
|
|||
int i, freed = 0;
|
||||
bool truncate_op = (lend == LLONG_MAX);
|
||||
|
||||
memset(&pseudo_vma, 0, sizeof(struct vm_area_struct));
|
||||
vma_init(&pseudo_vma, current->mm);
|
||||
pseudo_vma.vm_flags = (VM_HUGETLB | VM_MAYSHARE | VM_SHARED);
|
||||
pagevec_init(&pvec);
|
||||
|
@ -595,7 +594,6 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset,
|
|||
* allocation routines. If NUMA is configured, use page index
|
||||
* as input to create an allocation policy.
|
||||
*/
|
||||
memset(&pseudo_vma, 0, sizeof(struct vm_area_struct));
|
||||
vma_init(&pseudo_vma, mm);
|
||||
pseudo_vma.vm_flags = (VM_HUGETLB | VM_MAYSHARE | VM_SHARED);
|
||||
pseudo_vma.vm_file = file;
|
||||
|
|
|
@ -51,7 +51,7 @@ int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int nilfs_page_mkwrite(struct vm_fault *vmf)
|
||||
static vm_fault_t nilfs_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct page *page = vmf->page;
|
||||
|
|
|
@ -834,7 +834,7 @@ static int nilfs_setup_super(struct super_block *sb, int is_mount)
|
|||
sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT);
|
||||
|
||||
sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1);
|
||||
sbp[0]->s_mtime = cpu_to_le64(get_seconds());
|
||||
sbp[0]->s_mtime = cpu_to_le64(ktime_get_real_seconds());
|
||||
|
||||
skip_mount_setup:
|
||||
sbp[0]->s_state =
|
||||
|
|
|
@ -31,6 +31,7 @@ config PROC_FS
|
|||
config PROC_KCORE
|
||||
bool "/proc/kcore support" if !ARM
|
||||
depends on PROC_FS && MMU
|
||||
select CRASH_CORE
|
||||
help
|
||||
Provides a virtual ELF core file of the live kernel. This can
|
||||
be read with gdb and other ELF tools. No modifications can be
|
||||
|
|
|
@ -463,7 +463,7 @@ static int lstats_show_proc(struct seq_file *m, void *v)
|
|||
if (!task)
|
||||
return -ESRCH;
|
||||
seq_puts(m, "Latency Top version : v0.1\n");
|
||||
for (i = 0; i < 32; i++) {
|
||||
for (i = 0; i < LT_SAVECOUNT; i++) {
|
||||
struct latency_record *lr = &task->latency_record[i];
|
||||
if (lr->backtrace[0]) {
|
||||
int q;
|
||||
|
@ -1366,10 +1366,8 @@ static ssize_t proc_fail_nth_read(struct file *file, char __user *buf,
|
|||
if (!task)
|
||||
return -ESRCH;
|
||||
len = snprintf(numbuf, sizeof(numbuf), "%u\n", task->fail_nth);
|
||||
len = simple_read_from_buffer(buf, count, ppos, numbuf, len);
|
||||
put_task_struct(task);
|
||||
|
||||
return len;
|
||||
return simple_read_from_buffer(buf, count, ppos, numbuf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_fail_nth_operations = {
|
||||
|
@ -2519,47 +2517,47 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
|
|||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct inode * inode = file_inode(file);
|
||||
struct task_struct *task;
|
||||
void *page;
|
||||
ssize_t length;
|
||||
struct task_struct *task = get_proc_task(inode);
|
||||
|
||||
length = -ESRCH;
|
||||
if (!task)
|
||||
goto out_no_task;
|
||||
int rv;
|
||||
|
||||
rcu_read_lock();
|
||||
task = pid_task(proc_pid(inode), PIDTYPE_PID);
|
||||
if (!task) {
|
||||
rcu_read_unlock();
|
||||
return -ESRCH;
|
||||
}
|
||||
/* A task may only write its own attributes. */
|
||||
length = -EACCES;
|
||||
if (current != task)
|
||||
goto out;
|
||||
if (current != task) {
|
||||
rcu_read_unlock();
|
||||
return -EACCES;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (count > PAGE_SIZE)
|
||||
count = PAGE_SIZE;
|
||||
|
||||
/* No partial writes. */
|
||||
length = -EINVAL;
|
||||
if (*ppos != 0)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
page = memdup_user(buf, count);
|
||||
if (IS_ERR(page)) {
|
||||
length = PTR_ERR(page);
|
||||
rv = PTR_ERR(page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Guard against adverse ptrace interaction */
|
||||
length = mutex_lock_interruptible(¤t->signal->cred_guard_mutex);
|
||||
if (length < 0)
|
||||
rv = mutex_lock_interruptible(¤t->signal->cred_guard_mutex);
|
||||
if (rv < 0)
|
||||
goto out_free;
|
||||
|
||||
length = security_setprocattr(file->f_path.dentry->d_name.name,
|
||||
page, count);
|
||||
rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count);
|
||||
mutex_unlock(¤t->signal->cred_guard_mutex);
|
||||
out_free:
|
||||
kfree(page);
|
||||
out:
|
||||
put_task_struct(task);
|
||||
out_no_task:
|
||||
return length;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const struct file_operations proc_pid_attr_operations = {
|
||||
|
@ -3309,12 +3307,12 @@ static const struct pid_entry tid_base_stuff[] = {
|
|||
REG("cmdline", S_IRUGO, proc_pid_cmdline_ops),
|
||||
ONE("stat", S_IRUGO, proc_tid_stat),
|
||||
ONE("statm", S_IRUGO, proc_pid_statm),
|
||||
REG("maps", S_IRUGO, proc_tid_maps_operations),
|
||||
REG("maps", S_IRUGO, proc_pid_maps_operations),
|
||||
#ifdef CONFIG_PROC_CHILDREN
|
||||
REG("children", S_IRUGO, proc_tid_children_operations),
|
||||
#endif
|
||||
#ifdef CONFIG_NUMA
|
||||
REG("numa_maps", S_IRUGO, proc_tid_numa_maps_operations),
|
||||
REG("numa_maps", S_IRUGO, proc_pid_numa_maps_operations),
|
||||
#endif
|
||||
REG("mem", S_IRUSR|S_IWUSR, proc_mem_operations),
|
||||
LNK("cwd", proc_cwd_link),
|
||||
|
@ -3324,7 +3322,7 @@ static const struct pid_entry tid_base_stuff[] = {
|
|||
REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
|
||||
#ifdef CONFIG_PROC_PAGE_MONITOR
|
||||
REG("clear_refs", S_IWUSR, proc_clear_refs_operations),
|
||||
REG("smaps", S_IRUGO, proc_tid_smaps_operations),
|
||||
REG("smaps", S_IRUGO, proc_pid_smaps_operations),
|
||||
REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations),
|
||||
REG("pagemap", S_IRUSR, proc_pagemap_operations),
|
||||
#endif
|
||||
|
|
|
@ -286,9 +286,9 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,
|
|||
if (!dir_emit_dots(file, ctx))
|
||||
return 0;
|
||||
|
||||
i = ctx->pos - 2;
|
||||
read_lock(&proc_subdir_lock);
|
||||
de = pde_subdir_first(de);
|
||||
i = ctx->pos - 2;
|
||||
for (;;) {
|
||||
if (!de) {
|
||||
read_unlock(&proc_subdir_lock);
|
||||
|
@ -309,8 +309,8 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,
|
|||
pde_put(de);
|
||||
return 0;
|
||||
}
|
||||
read_lock(&proc_subdir_lock);
|
||||
ctx->pos++;
|
||||
read_lock(&proc_subdir_lock);
|
||||
next = pde_subdir_next(de);
|
||||
pde_put(de);
|
||||
de = next;
|
||||
|
|
|
@ -105,8 +105,10 @@ void __init proc_init_kmemcache(void)
|
|||
kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0,
|
||||
SLAB_ACCOUNT|SLAB_PANIC, NULL);
|
||||
proc_dir_entry_cache = kmem_cache_create_usercopy(
|
||||
"proc_dir_entry", SIZEOF_PDE_SLOT, 0, SLAB_PANIC,
|
||||
OFFSETOF_PDE_NAME, SIZEOF_PDE_INLINE_NAME, NULL);
|
||||
"proc_dir_entry", SIZEOF_PDE, 0, SLAB_PANIC,
|
||||
offsetof(struct proc_dir_entry, inline_name),
|
||||
SIZEOF_PDE_INLINE_NAME, NULL);
|
||||
BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE);
|
||||
}
|
||||
|
||||
static int proc_show_options(struct seq_file *seq, struct dentry *root)
|
||||
|
|
|
@ -65,16 +65,13 @@ struct proc_dir_entry {
|
|||
char inline_name[];
|
||||
} __randomize_layout;
|
||||
|
||||
#define OFFSETOF_PDE_NAME offsetof(struct proc_dir_entry, inline_name)
|
||||
#define SIZEOF_PDE_SLOT \
|
||||
(OFFSETOF_PDE_NAME + 34 <= 64 ? 64 : \
|
||||
OFFSETOF_PDE_NAME + 34 <= 128 ? 128 : \
|
||||
OFFSETOF_PDE_NAME + 34 <= 192 ? 192 : \
|
||||
OFFSETOF_PDE_NAME + 34 <= 256 ? 256 : \
|
||||
OFFSETOF_PDE_NAME + 34 <= 512 ? 512 : \
|
||||
0)
|
||||
|
||||
#define SIZEOF_PDE_INLINE_NAME (SIZEOF_PDE_SLOT - OFFSETOF_PDE_NAME)
|
||||
#define SIZEOF_PDE ( \
|
||||
sizeof(struct proc_dir_entry) < 128 ? 128 : \
|
||||
sizeof(struct proc_dir_entry) < 192 ? 192 : \
|
||||
sizeof(struct proc_dir_entry) < 256 ? 256 : \
|
||||
sizeof(struct proc_dir_entry) < 512 ? 512 : \
|
||||
0)
|
||||
#define SIZEOF_PDE_INLINE_NAME (SIZEOF_PDE - sizeof(struct proc_dir_entry))
|
||||
|
||||
extern struct kmem_cache *proc_dir_entry_cache;
|
||||
void pde_free(struct proc_dir_entry *pde);
|
||||
|
@ -116,12 +113,12 @@ static inline void *__PDE_DATA(const struct inode *inode)
|
|||
return PDE(inode)->data;
|
||||
}
|
||||
|
||||
static inline struct pid *proc_pid(struct inode *inode)
|
||||
static inline struct pid *proc_pid(const struct inode *inode)
|
||||
{
|
||||
return PROC_I(inode)->pid;
|
||||
}
|
||||
|
||||
static inline struct task_struct *get_proc_task(struct inode *inode)
|
||||
static inline struct task_struct *get_proc_task(const struct inode *inode)
|
||||
{
|
||||
return get_pid_task(proc_pid(inode), PIDTYPE_PID);
|
||||
}
|
||||
|
@ -285,7 +282,6 @@ struct proc_maps_private {
|
|||
struct inode *inode;
|
||||
struct task_struct *task;
|
||||
struct mm_struct *mm;
|
||||
struct mem_size_stats *rollup;
|
||||
#ifdef CONFIG_MMU
|
||||
struct vm_area_struct *tail_vma;
|
||||
#endif
|
||||
|
@ -297,12 +293,9 @@ struct proc_maps_private {
|
|||
struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode);
|
||||
|
||||
extern const struct file_operations proc_pid_maps_operations;
|
||||
extern const struct file_operations proc_tid_maps_operations;
|
||||
extern const struct file_operations proc_pid_numa_maps_operations;
|
||||
extern const struct file_operations proc_tid_numa_maps_operations;
|
||||
extern const struct file_operations proc_pid_smaps_operations;
|
||||
extern const struct file_operations proc_pid_smaps_rollup_operations;
|
||||
extern const struct file_operations proc_tid_smaps_operations;
|
||||
extern const struct file_operations proc_clear_refs_operations;
|
||||
extern const struct file_operations proc_pagemap_operations;
|
||||
|
||||
|
|
534
fs/proc/kcore.c
534
fs/proc/kcore.c
|
@ -10,6 +10,7 @@
|
|||
* Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar <kanoj@sgi.com>
|
||||
*/
|
||||
|
||||
#include <linux/crash_core.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kcore.h>
|
||||
|
@ -49,32 +50,23 @@ static struct proc_dir_entry *proc_root_kcore;
|
|||
#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
|
||||
#endif
|
||||
|
||||
/* An ELF note in memory */
|
||||
struct memelfnote
|
||||
{
|
||||
const char *name;
|
||||
int type;
|
||||
unsigned int datasz;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static LIST_HEAD(kclist_head);
|
||||
static DEFINE_RWLOCK(kclist_lock);
|
||||
static DECLARE_RWSEM(kclist_lock);
|
||||
static int kcore_need_update = 1;
|
||||
|
||||
void
|
||||
kclist_add(struct kcore_list *new, void *addr, size_t size, int type)
|
||||
/* This doesn't grab kclist_lock, so it should only be used at init time. */
|
||||
void __init kclist_add(struct kcore_list *new, void *addr, size_t size,
|
||||
int type)
|
||||
{
|
||||
new->addr = (unsigned long)addr;
|
||||
new->size = size;
|
||||
new->type = type;
|
||||
|
||||
write_lock(&kclist_lock);
|
||||
list_add_tail(&new->list, &kclist_head);
|
||||
write_unlock(&kclist_lock);
|
||||
}
|
||||
|
||||
static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
|
||||
static size_t get_kcore_size(int *nphdr, size_t *phdrs_len, size_t *notes_len,
|
||||
size_t *data_offset)
|
||||
{
|
||||
size_t try, size;
|
||||
struct kcore_list *m;
|
||||
|
@ -88,65 +80,29 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
|
|||
size = try;
|
||||
*nphdr = *nphdr + 1;
|
||||
}
|
||||
*elf_buflen = sizeof(struct elfhdr) +
|
||||
(*nphdr + 2)*sizeof(struct elf_phdr) +
|
||||
3 * ((sizeof(struct elf_note)) +
|
||||
roundup(sizeof(CORE_STR), 4)) +
|
||||
roundup(sizeof(struct elf_prstatus), 4) +
|
||||
roundup(sizeof(struct elf_prpsinfo), 4) +
|
||||
roundup(arch_task_struct_size, 4);
|
||||
*elf_buflen = PAGE_ALIGN(*elf_buflen);
|
||||
return size + *elf_buflen;
|
||||
|
||||
*phdrs_len = *nphdr * sizeof(struct elf_phdr);
|
||||
*notes_len = (4 * sizeof(struct elf_note) +
|
||||
3 * ALIGN(sizeof(CORE_STR), 4) +
|
||||
VMCOREINFO_NOTE_NAME_BYTES +
|
||||
ALIGN(sizeof(struct elf_prstatus), 4) +
|
||||
ALIGN(sizeof(struct elf_prpsinfo), 4) +
|
||||
ALIGN(arch_task_struct_size, 4) +
|
||||
ALIGN(vmcoreinfo_size, 4));
|
||||
*data_offset = PAGE_ALIGN(sizeof(struct elfhdr) + *phdrs_len +
|
||||
*notes_len);
|
||||
return *data_offset + size;
|
||||
}
|
||||
|
||||
static void free_kclist_ents(struct list_head *head)
|
||||
{
|
||||
struct kcore_list *tmp, *pos;
|
||||
|
||||
list_for_each_entry_safe(pos, tmp, head, list) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Replace all KCORE_RAM/KCORE_VMEMMAP information with passed list.
|
||||
*/
|
||||
static void __kcore_update_ram(struct list_head *list)
|
||||
{
|
||||
int nphdr;
|
||||
size_t size;
|
||||
struct kcore_list *tmp, *pos;
|
||||
LIST_HEAD(garbage);
|
||||
|
||||
write_lock(&kclist_lock);
|
||||
if (kcore_need_update) {
|
||||
list_for_each_entry_safe(pos, tmp, &kclist_head, list) {
|
||||
if (pos->type == KCORE_RAM
|
||||
|| pos->type == KCORE_VMEMMAP)
|
||||
list_move(&pos->list, &garbage);
|
||||
}
|
||||
list_splice_tail(list, &kclist_head);
|
||||
} else
|
||||
list_splice(list, &garbage);
|
||||
kcore_need_update = 0;
|
||||
proc_root_kcore->size = get_kcore_size(&nphdr, &size);
|
||||
write_unlock(&kclist_lock);
|
||||
|
||||
free_kclist_ents(&garbage);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
/*
|
||||
* If no highmem, we can assume [0...max_low_pfn) continuous range of memory
|
||||
* because memory hole is not as big as !HIGHMEM case.
|
||||
* (HIGHMEM is special because part of memory is _invisible_ from the kernel.)
|
||||
*/
|
||||
static int kcore_update_ram(void)
|
||||
static int kcore_ram_list(struct list_head *head)
|
||||
{
|
||||
LIST_HEAD(head);
|
||||
struct kcore_list *ent;
|
||||
int ret = 0;
|
||||
|
||||
ent = kmalloc(sizeof(*ent), GFP_KERNEL);
|
||||
if (!ent)
|
||||
|
@ -154,9 +110,8 @@ static int kcore_update_ram(void)
|
|||
ent->addr = (unsigned long)__va(0);
|
||||
ent->size = max_low_pfn << PAGE_SHIFT;
|
||||
ent->type = KCORE_RAM;
|
||||
list_add(&ent->list, &head);
|
||||
__kcore_update_ram(&head);
|
||||
return ret;
|
||||
list_add(&ent->list, head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_HIGHMEM */
|
||||
|
@ -255,11 +210,10 @@ kclist_add_private(unsigned long pfn, unsigned long nr_pages, void *arg)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int kcore_update_ram(void)
|
||||
static int kcore_ram_list(struct list_head *list)
|
||||
{
|
||||
int nid, ret;
|
||||
unsigned long end_pfn;
|
||||
LIST_HEAD(head);
|
||||
|
||||
/* Not inialized....update now */
|
||||
/* find out "max pfn" */
|
||||
|
@ -271,258 +225,255 @@ static int kcore_update_ram(void)
|
|||
end_pfn = node_end;
|
||||
}
|
||||
/* scan 0 to max_pfn */
|
||||
ret = walk_system_ram_range(0, end_pfn, &head, kclist_add_private);
|
||||
if (ret) {
|
||||
free_kclist_ents(&head);
|
||||
ret = walk_system_ram_range(0, end_pfn, list, kclist_add_private);
|
||||
if (ret)
|
||||
return -ENOMEM;
|
||||
}
|
||||
__kcore_update_ram(&head);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* determine size of ELF note
|
||||
*/
|
||||
static int notesize(struct memelfnote *en)
|
||||
static int kcore_update_ram(void)
|
||||
{
|
||||
int sz;
|
||||
LIST_HEAD(list);
|
||||
LIST_HEAD(garbage);
|
||||
int nphdr;
|
||||
size_t phdrs_len, notes_len, data_offset;
|
||||
struct kcore_list *tmp, *pos;
|
||||
int ret = 0;
|
||||
|
||||
sz = sizeof(struct elf_note);
|
||||
sz += roundup((strlen(en->name) + 1), 4);
|
||||
sz += roundup(en->datasz, 4);
|
||||
down_write(&kclist_lock);
|
||||
if (!xchg(&kcore_need_update, 0))
|
||||
goto out;
|
||||
|
||||
return sz;
|
||||
} /* end notesize() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* store a note in the header buffer
|
||||
*/
|
||||
static char *storenote(struct memelfnote *men, char *bufp)
|
||||
{
|
||||
struct elf_note en;
|
||||
|
||||
#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
|
||||
|
||||
en.n_namesz = strlen(men->name) + 1;
|
||||
en.n_descsz = men->datasz;
|
||||
en.n_type = men->type;
|
||||
|
||||
DUMP_WRITE(&en, sizeof(en));
|
||||
DUMP_WRITE(men->name, en.n_namesz);
|
||||
|
||||
/* XXX - cast from long long to long to avoid need for libgcc.a */
|
||||
bufp = (char*) roundup((unsigned long)bufp,4);
|
||||
DUMP_WRITE(men->data, men->datasz);
|
||||
bufp = (char*) roundup((unsigned long)bufp,4);
|
||||
|
||||
#undef DUMP_WRITE
|
||||
|
||||
return bufp;
|
||||
} /* end storenote() */
|
||||
|
||||
/*
|
||||
* store an ELF coredump header in the supplied buffer
|
||||
* nphdr is the number of elf_phdr to insert
|
||||
*/
|
||||
static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
|
||||
{
|
||||
struct elf_prstatus prstatus; /* NT_PRSTATUS */
|
||||
struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
|
||||
struct elf_phdr *nhdr, *phdr;
|
||||
struct elfhdr *elf;
|
||||
struct memelfnote notes[3];
|
||||
off_t offset = 0;
|
||||
struct kcore_list *m;
|
||||
|
||||
/* setup ELF header */
|
||||
elf = (struct elfhdr *) bufp;
|
||||
bufp += sizeof(struct elfhdr);
|
||||
offset += sizeof(struct elfhdr);
|
||||
memcpy(elf->e_ident, ELFMAG, SELFMAG);
|
||||
elf->e_ident[EI_CLASS] = ELF_CLASS;
|
||||
elf->e_ident[EI_DATA] = ELF_DATA;
|
||||
elf->e_ident[EI_VERSION]= EV_CURRENT;
|
||||
elf->e_ident[EI_OSABI] = ELF_OSABI;
|
||||
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
elf->e_type = ET_CORE;
|
||||
elf->e_machine = ELF_ARCH;
|
||||
elf->e_version = EV_CURRENT;
|
||||
elf->e_entry = 0;
|
||||
elf->e_phoff = sizeof(struct elfhdr);
|
||||
elf->e_shoff = 0;
|
||||
elf->e_flags = ELF_CORE_EFLAGS;
|
||||
elf->e_ehsize = sizeof(struct elfhdr);
|
||||
elf->e_phentsize= sizeof(struct elf_phdr);
|
||||
elf->e_phnum = nphdr;
|
||||
elf->e_shentsize= 0;
|
||||
elf->e_shnum = 0;
|
||||
elf->e_shstrndx = 0;
|
||||
|
||||
/* setup ELF PT_NOTE program header */
|
||||
nhdr = (struct elf_phdr *) bufp;
|
||||
bufp += sizeof(struct elf_phdr);
|
||||
offset += sizeof(struct elf_phdr);
|
||||
nhdr->p_type = PT_NOTE;
|
||||
nhdr->p_offset = 0;
|
||||
nhdr->p_vaddr = 0;
|
||||
nhdr->p_paddr = 0;
|
||||
nhdr->p_filesz = 0;
|
||||
nhdr->p_memsz = 0;
|
||||
nhdr->p_flags = 0;
|
||||
nhdr->p_align = 0;
|
||||
|
||||
/* setup ELF PT_LOAD program header for every area */
|
||||
list_for_each_entry(m, &kclist_head, list) {
|
||||
phdr = (struct elf_phdr *) bufp;
|
||||
bufp += sizeof(struct elf_phdr);
|
||||
offset += sizeof(struct elf_phdr);
|
||||
|
||||
phdr->p_type = PT_LOAD;
|
||||
phdr->p_flags = PF_R|PF_W|PF_X;
|
||||
phdr->p_offset = kc_vaddr_to_offset(m->addr) + dataoff;
|
||||
phdr->p_vaddr = (size_t)m->addr;
|
||||
if (m->type == KCORE_RAM || m->type == KCORE_TEXT)
|
||||
phdr->p_paddr = __pa(m->addr);
|
||||
else
|
||||
phdr->p_paddr = (elf_addr_t)-1;
|
||||
phdr->p_filesz = phdr->p_memsz = m->size;
|
||||
phdr->p_align = PAGE_SIZE;
|
||||
ret = kcore_ram_list(&list);
|
||||
if (ret) {
|
||||
/* Couldn't get the RAM list, try again next time. */
|
||||
WRITE_ONCE(kcore_need_update, 1);
|
||||
list_splice_tail(&list, &garbage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the notes in similar form to SVR4 core dumps made
|
||||
* with info from their /proc.
|
||||
*/
|
||||
nhdr->p_offset = offset;
|
||||
list_for_each_entry_safe(pos, tmp, &kclist_head, list) {
|
||||
if (pos->type == KCORE_RAM || pos->type == KCORE_VMEMMAP)
|
||||
list_move(&pos->list, &garbage);
|
||||
}
|
||||
list_splice_tail(&list, &kclist_head);
|
||||
|
||||
/* set up the process status */
|
||||
notes[0].name = CORE_STR;
|
||||
notes[0].type = NT_PRSTATUS;
|
||||
notes[0].datasz = sizeof(struct elf_prstatus);
|
||||
notes[0].data = &prstatus;
|
||||
proc_root_kcore->size = get_kcore_size(&nphdr, &phdrs_len, ¬es_len,
|
||||
&data_offset);
|
||||
|
||||
memset(&prstatus, 0, sizeof(struct elf_prstatus));
|
||||
out:
|
||||
up_write(&kclist_lock);
|
||||
list_for_each_entry_safe(pos, tmp, &garbage, list) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
nhdr->p_filesz = notesize(¬es[0]);
|
||||
bufp = storenote(¬es[0], bufp);
|
||||
static void append_kcore_note(char *notes, size_t *i, const char *name,
|
||||
unsigned int type, const void *desc,
|
||||
size_t descsz)
|
||||
{
|
||||
struct elf_note *note = (struct elf_note *)¬es[*i];
|
||||
|
||||
/* set up the process info */
|
||||
notes[1].name = CORE_STR;
|
||||
notes[1].type = NT_PRPSINFO;
|
||||
notes[1].datasz = sizeof(struct elf_prpsinfo);
|
||||
notes[1].data = &prpsinfo;
|
||||
note->n_namesz = strlen(name) + 1;
|
||||
note->n_descsz = descsz;
|
||||
note->n_type = type;
|
||||
*i += sizeof(*note);
|
||||
memcpy(¬es[*i], name, note->n_namesz);
|
||||
*i = ALIGN(*i + note->n_namesz, 4);
|
||||
memcpy(¬es[*i], desc, descsz);
|
||||
*i = ALIGN(*i + descsz, 4);
|
||||
}
|
||||
|
||||
memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo));
|
||||
prpsinfo.pr_state = 0;
|
||||
prpsinfo.pr_sname = 'R';
|
||||
prpsinfo.pr_zomb = 0;
|
||||
|
||||
strcpy(prpsinfo.pr_fname, "vmlinux");
|
||||
strlcpy(prpsinfo.pr_psargs, saved_command_line, sizeof(prpsinfo.pr_psargs));
|
||||
|
||||
nhdr->p_filesz += notesize(¬es[1]);
|
||||
bufp = storenote(¬es[1], bufp);
|
||||
|
||||
/* set up the task structure */
|
||||
notes[2].name = CORE_STR;
|
||||
notes[2].type = NT_TASKSTRUCT;
|
||||
notes[2].datasz = arch_task_struct_size;
|
||||
notes[2].data = current;
|
||||
|
||||
nhdr->p_filesz += notesize(¬es[2]);
|
||||
bufp = storenote(¬es[2], bufp);
|
||||
|
||||
} /* end elf_kcore_store_hdr() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* read from the ELF header and then kernel memory
|
||||
*/
|
||||
static ssize_t
|
||||
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
|
||||
{
|
||||
char *buf = file->private_data;
|
||||
ssize_t acc = 0;
|
||||
size_t size, tsz;
|
||||
size_t elf_buflen;
|
||||
size_t phdrs_offset, notes_offset, data_offset;
|
||||
size_t phdrs_len, notes_len;
|
||||
struct kcore_list *m;
|
||||
size_t tsz;
|
||||
int nphdr;
|
||||
unsigned long start;
|
||||
size_t orig_buflen = buflen;
|
||||
int ret = 0;
|
||||
|
||||
read_lock(&kclist_lock);
|
||||
size = get_kcore_size(&nphdr, &elf_buflen);
|
||||
down_read(&kclist_lock);
|
||||
|
||||
if (buflen == 0 || *fpos >= size) {
|
||||
read_unlock(&kclist_lock);
|
||||
return 0;
|
||||
}
|
||||
get_kcore_size(&nphdr, &phdrs_len, ¬es_len, &data_offset);
|
||||
phdrs_offset = sizeof(struct elfhdr);
|
||||
notes_offset = phdrs_offset + phdrs_len;
|
||||
|
||||
/* trim buflen to not go beyond EOF */
|
||||
if (buflen > size - *fpos)
|
||||
buflen = size - *fpos;
|
||||
/* ELF file header. */
|
||||
if (buflen && *fpos < sizeof(struct elfhdr)) {
|
||||
struct elfhdr ehdr = {
|
||||
.e_ident = {
|
||||
[EI_MAG0] = ELFMAG0,
|
||||
[EI_MAG1] = ELFMAG1,
|
||||
[EI_MAG2] = ELFMAG2,
|
||||
[EI_MAG3] = ELFMAG3,
|
||||
[EI_CLASS] = ELF_CLASS,
|
||||
[EI_DATA] = ELF_DATA,
|
||||
[EI_VERSION] = EV_CURRENT,
|
||||
[EI_OSABI] = ELF_OSABI,
|
||||
},
|
||||
.e_type = ET_CORE,
|
||||
.e_machine = ELF_ARCH,
|
||||
.e_version = EV_CURRENT,
|
||||
.e_phoff = sizeof(struct elfhdr),
|
||||
.e_flags = ELF_CORE_EFLAGS,
|
||||
.e_ehsize = sizeof(struct elfhdr),
|
||||
.e_phentsize = sizeof(struct elf_phdr),
|
||||
.e_phnum = nphdr,
|
||||
};
|
||||
|
||||
/* construct an ELF core header if we'll need some of it */
|
||||
if (*fpos < elf_buflen) {
|
||||
char * elf_buf;
|
||||
|
||||
tsz = elf_buflen - *fpos;
|
||||
if (buflen < tsz)
|
||||
tsz = buflen;
|
||||
elf_buf = kzalloc(elf_buflen, GFP_ATOMIC);
|
||||
if (!elf_buf) {
|
||||
read_unlock(&kclist_lock);
|
||||
return -ENOMEM;
|
||||
tsz = min_t(size_t, buflen, sizeof(struct elfhdr) - *fpos);
|
||||
if (copy_to_user(buffer, (char *)&ehdr + *fpos, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen);
|
||||
read_unlock(&kclist_lock);
|
||||
if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
|
||||
kfree(elf_buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
kfree(elf_buf);
|
||||
|
||||
buffer += tsz;
|
||||
buflen -= tsz;
|
||||
*fpos += tsz;
|
||||
buffer += tsz;
|
||||
acc += tsz;
|
||||
}
|
||||
|
||||
/* leave now if filled buffer already */
|
||||
if (buflen == 0)
|
||||
return acc;
|
||||
} else
|
||||
read_unlock(&kclist_lock);
|
||||
/* ELF program headers. */
|
||||
if (buflen && *fpos < phdrs_offset + phdrs_len) {
|
||||
struct elf_phdr *phdrs, *phdr;
|
||||
|
||||
phdrs = kzalloc(phdrs_len, GFP_KERNEL);
|
||||
if (!phdrs) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phdrs[0].p_type = PT_NOTE;
|
||||
phdrs[0].p_offset = notes_offset;
|
||||
phdrs[0].p_filesz = notes_len;
|
||||
|
||||
phdr = &phdrs[1];
|
||||
list_for_each_entry(m, &kclist_head, list) {
|
||||
phdr->p_type = PT_LOAD;
|
||||
phdr->p_flags = PF_R | PF_W | PF_X;
|
||||
phdr->p_offset = kc_vaddr_to_offset(m->addr) + data_offset;
|
||||
phdr->p_vaddr = (size_t)m->addr;
|
||||
if (m->type == KCORE_RAM)
|
||||
phdr->p_paddr = __pa(m->addr);
|
||||
else if (m->type == KCORE_TEXT)
|
||||
phdr->p_paddr = __pa_symbol(m->addr);
|
||||
else
|
||||
phdr->p_paddr = (elf_addr_t)-1;
|
||||
phdr->p_filesz = phdr->p_memsz = m->size;
|
||||
phdr->p_align = PAGE_SIZE;
|
||||
phdr++;
|
||||
}
|
||||
|
||||
tsz = min_t(size_t, buflen, phdrs_offset + phdrs_len - *fpos);
|
||||
if (copy_to_user(buffer, (char *)phdrs + *fpos - phdrs_offset,
|
||||
tsz)) {
|
||||
kfree(phdrs);
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
kfree(phdrs);
|
||||
|
||||
buffer += tsz;
|
||||
buflen -= tsz;
|
||||
*fpos += tsz;
|
||||
}
|
||||
|
||||
/* ELF note segment. */
|
||||
if (buflen && *fpos < notes_offset + notes_len) {
|
||||
struct elf_prstatus prstatus = {};
|
||||
struct elf_prpsinfo prpsinfo = {
|
||||
.pr_sname = 'R',
|
||||
.pr_fname = "vmlinux",
|
||||
};
|
||||
char *notes;
|
||||
size_t i = 0;
|
||||
|
||||
strlcpy(prpsinfo.pr_psargs, saved_command_line,
|
||||
sizeof(prpsinfo.pr_psargs));
|
||||
|
||||
notes = kzalloc(notes_len, GFP_KERNEL);
|
||||
if (!notes) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
append_kcore_note(notes, &i, CORE_STR, NT_PRSTATUS, &prstatus,
|
||||
sizeof(prstatus));
|
||||
append_kcore_note(notes, &i, CORE_STR, NT_PRPSINFO, &prpsinfo,
|
||||
sizeof(prpsinfo));
|
||||
append_kcore_note(notes, &i, CORE_STR, NT_TASKSTRUCT, current,
|
||||
arch_task_struct_size);
|
||||
/*
|
||||
* vmcoreinfo_size is mostly constant after init time, but it
|
||||
* can be changed by crash_save_vmcoreinfo(). Racing here with a
|
||||
* panic on another CPU before the machine goes down is insanely
|
||||
* unlikely, but it's better to not leave potential buffer
|
||||
* overflows lying around, regardless.
|
||||
*/
|
||||
append_kcore_note(notes, &i, VMCOREINFO_NOTE_NAME, 0,
|
||||
vmcoreinfo_data,
|
||||
min(vmcoreinfo_size, notes_len - i));
|
||||
|
||||
tsz = min_t(size_t, buflen, notes_offset + notes_len - *fpos);
|
||||
if (copy_to_user(buffer, notes + *fpos - notes_offset, tsz)) {
|
||||
kfree(notes);
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
kfree(notes);
|
||||
|
||||
buffer += tsz;
|
||||
buflen -= tsz;
|
||||
*fpos += tsz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if our file offset matches with any of
|
||||
* the addresses in the elf_phdr on our list.
|
||||
*/
|
||||
start = kc_offset_to_vaddr(*fpos - elf_buflen);
|
||||
start = kc_offset_to_vaddr(*fpos - data_offset);
|
||||
if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
|
||||
tsz = buflen;
|
||||
|
||||
while (buflen) {
|
||||
struct kcore_list *m;
|
||||
|
||||
read_lock(&kclist_lock);
|
||||
list_for_each_entry(m, &kclist_head, list) {
|
||||
if (start >= m->addr && start < (m->addr+m->size))
|
||||
break;
|
||||
m = NULL;
|
||||
while (buflen) {
|
||||
/*
|
||||
* If this is the first iteration or the address is not within
|
||||
* the previous entry, search for a matching entry.
|
||||
*/
|
||||
if (!m || start < m->addr || start >= m->addr + m->size) {
|
||||
list_for_each_entry(m, &kclist_head, list) {
|
||||
if (start >= m->addr &&
|
||||
start < m->addr + m->size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&kclist_lock);
|
||||
|
||||
if (&m->list == &kclist_head) {
|
||||
if (clear_user(buffer, tsz))
|
||||
return -EFAULT;
|
||||
if (clear_user(buffer, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else if (m->type == KCORE_VMALLOC) {
|
||||
vread(buf, (char *)start, tsz);
|
||||
/* we have to zero-fill user buffer even if no read */
|
||||
if (copy_to_user(buffer, buf, tsz))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(buffer, buf, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else if (m->type == KCORE_USER) {
|
||||
/* User page is handled prior to normal kernel page: */
|
||||
if (copy_to_user(buffer, (char *)start, tsz))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(buffer, (char *)start, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (kern_addr_valid(start)) {
|
||||
/*
|
||||
|
@ -530,29 +481,37 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
|
|||
* hardened user copy kernel text checks.
|
||||
*/
|
||||
if (probe_kernel_read(buf, (void *) start, tsz)) {
|
||||
if (clear_user(buffer, tsz))
|
||||
return -EFAULT;
|
||||
if (clear_user(buffer, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (copy_to_user(buffer, buf, tsz))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(buffer, buf, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (clear_user(buffer, tsz))
|
||||
return -EFAULT;
|
||||
if (clear_user(buffer, tsz)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
buflen -= tsz;
|
||||
*fpos += tsz;
|
||||
buffer += tsz;
|
||||
acc += tsz;
|
||||
start += tsz;
|
||||
tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
|
||||
}
|
||||
|
||||
return acc;
|
||||
out:
|
||||
up_read(&kclist_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
return orig_buflen - buflen;
|
||||
}
|
||||
|
||||
|
||||
static int open_kcore(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
|
@ -592,9 +551,8 @@ static int __meminit kcore_callback(struct notifier_block *self,
|
|||
switch (action) {
|
||||
case MEM_ONLINE:
|
||||
case MEM_OFFLINE:
|
||||
write_lock(&kclist_lock);
|
||||
kcore_need_update = 1;
|
||||
write_unlock(&kclist_lock);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/mman.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/quicklist.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/swap.h>
|
||||
|
@ -121,6 +122,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
|
|||
(unsigned long)VMALLOC_TOTAL >> 10);
|
||||
show_val_kb(m, "VmallocUsed: ", 0ul);
|
||||
show_val_kb(m, "VmallocChunk: ", 0ul);
|
||||
show_val_kb(m, "Percpu: ", pcpu_nr_pages());
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
seq_printf(m, "HardwareCorrupted: %5lu kB\n",
|
||||
|
|
|
@ -183,7 +183,7 @@ static int show_stat(struct seq_file *p, void *v)
|
|||
|
||||
static int stat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
size_t size = 1024 + 128 * num_online_cpus();
|
||||
unsigned int size = 1024 + 128 * num_online_cpus();
|
||||
|
||||
/* minimum size to display an interrupt count : 2 bytes */
|
||||
size += 2 * nr_irqs;
|
||||
|
|
|
@ -247,7 +247,6 @@ static int proc_map_release(struct inode *inode, struct file *file)
|
|||
if (priv->mm)
|
||||
mmdrop(priv->mm);
|
||||
|
||||
kfree(priv->rollup);
|
||||
return seq_release_private(inode, file);
|
||||
}
|
||||
|
||||
|
@ -294,7 +293,7 @@ static void show_vma_header_prefix(struct seq_file *m,
|
|||
}
|
||||
|
||||
static void
|
||||
show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
|
||||
show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
struct file *file = vma->vm_file;
|
||||
|
@ -357,35 +356,18 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
|
|||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
static int show_map(struct seq_file *m, void *v, int is_pid)
|
||||
static int show_map(struct seq_file *m, void *v)
|
||||
{
|
||||
show_map_vma(m, v, is_pid);
|
||||
show_map_vma(m, v);
|
||||
m_cache_vma(m, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_pid_map(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_map(m, v, 1);
|
||||
}
|
||||
|
||||
static int show_tid_map(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_map(m, v, 0);
|
||||
}
|
||||
|
||||
static const struct seq_operations proc_pid_maps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_pid_map
|
||||
};
|
||||
|
||||
static const struct seq_operations proc_tid_maps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_tid_map
|
||||
.show = show_map
|
||||
};
|
||||
|
||||
static int pid_maps_open(struct inode *inode, struct file *file)
|
||||
|
@ -393,11 +375,6 @@ static int pid_maps_open(struct inode *inode, struct file *file)
|
|||
return do_maps_open(inode, file, &proc_pid_maps_op);
|
||||
}
|
||||
|
||||
static int tid_maps_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return do_maps_open(inode, file, &proc_tid_maps_op);
|
||||
}
|
||||
|
||||
const struct file_operations proc_pid_maps_operations = {
|
||||
.open = pid_maps_open,
|
||||
.read = seq_read,
|
||||
|
@ -405,13 +382,6 @@ const struct file_operations proc_pid_maps_operations = {
|
|||
.release = proc_map_release,
|
||||
};
|
||||
|
||||
const struct file_operations proc_tid_maps_operations = {
|
||||
.open = tid_maps_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = proc_map_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Proportional Set Size(PSS): my share of RSS.
|
||||
*
|
||||
|
@ -433,7 +403,6 @@ const struct file_operations proc_tid_maps_operations = {
|
|||
|
||||
#ifdef CONFIG_PROC_PAGE_MONITOR
|
||||
struct mem_size_stats {
|
||||
bool first;
|
||||
unsigned long resident;
|
||||
unsigned long shared_clean;
|
||||
unsigned long shared_dirty;
|
||||
|
@ -447,7 +416,6 @@ struct mem_size_stats {
|
|||
unsigned long swap;
|
||||
unsigned long shared_hugetlb;
|
||||
unsigned long private_hugetlb;
|
||||
unsigned long first_vma_start;
|
||||
u64 pss;
|
||||
u64 pss_locked;
|
||||
u64 swap_pss;
|
||||
|
@ -731,14 +699,9 @@ static int smaps_hugetlb_range(pte_t *pte, unsigned long hmask,
|
|||
}
|
||||
#endif /* HUGETLB_PAGE */
|
||||
|
||||
#define SEQ_PUT_DEC(str, val) \
|
||||
seq_put_decimal_ull_width(m, str, (val) >> 10, 8)
|
||||
static int show_smap(struct seq_file *m, void *v, int is_pid)
|
||||
static void smap_gather_stats(struct vm_area_struct *vma,
|
||||
struct mem_size_stats *mss)
|
||||
{
|
||||
struct proc_maps_private *priv = m->private;
|
||||
struct vm_area_struct *vma = v;
|
||||
struct mem_size_stats mss_stack;
|
||||
struct mem_size_stats *mss;
|
||||
struct mm_walk smaps_walk = {
|
||||
.pmd_entry = smaps_pte_range,
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
|
@ -746,23 +709,6 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
|
|||
#endif
|
||||
.mm = vma->vm_mm,
|
||||
};
|
||||
int ret = 0;
|
||||
bool rollup_mode;
|
||||
bool last_vma;
|
||||
|
||||
if (priv->rollup) {
|
||||
rollup_mode = true;
|
||||
mss = priv->rollup;
|
||||
if (mss->first) {
|
||||
mss->first_vma_start = vma->vm_start;
|
||||
mss->first = false;
|
||||
}
|
||||
last_vma = !m_next_vma(priv, vma);
|
||||
} else {
|
||||
rollup_mode = false;
|
||||
memset(&mss_stack, 0, sizeof(mss_stack));
|
||||
mss = &mss_stack;
|
||||
}
|
||||
|
||||
smaps_walk.private = mss;
|
||||
|
||||
|
@ -794,79 +740,116 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
|
|||
walk_page_vma(vma, &smaps_walk);
|
||||
if (vma->vm_flags & VM_LOCKED)
|
||||
mss->pss_locked += mss->pss;
|
||||
}
|
||||
|
||||
if (!rollup_mode) {
|
||||
show_map_vma(m, vma, is_pid);
|
||||
} else if (last_vma) {
|
||||
show_vma_header_prefix(
|
||||
m, mss->first_vma_start, vma->vm_end, 0, 0, 0, 0);
|
||||
seq_pad(m, ' ');
|
||||
seq_puts(m, "[rollup]\n");
|
||||
} else {
|
||||
ret = SEQ_SKIP;
|
||||
}
|
||||
#define SEQ_PUT_DEC(str, val) \
|
||||
seq_put_decimal_ull_width(m, str, (val) >> 10, 8)
|
||||
|
||||
if (!rollup_mode) {
|
||||
SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start);
|
||||
SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma));
|
||||
SEQ_PUT_DEC(" kB\nMMUPageSize: ", vma_mmu_pagesize(vma));
|
||||
seq_puts(m, " kB\n");
|
||||
}
|
||||
/* Show the contents common for smaps and smaps_rollup */
|
||||
static void __show_smap(struct seq_file *m, const struct mem_size_stats *mss)
|
||||
{
|
||||
SEQ_PUT_DEC("Rss: ", mss->resident);
|
||||
SEQ_PUT_DEC(" kB\nPss: ", mss->pss >> PSS_SHIFT);
|
||||
SEQ_PUT_DEC(" kB\nShared_Clean: ", mss->shared_clean);
|
||||
SEQ_PUT_DEC(" kB\nShared_Dirty: ", mss->shared_dirty);
|
||||
SEQ_PUT_DEC(" kB\nPrivate_Clean: ", mss->private_clean);
|
||||
SEQ_PUT_DEC(" kB\nPrivate_Dirty: ", mss->private_dirty);
|
||||
SEQ_PUT_DEC(" kB\nReferenced: ", mss->referenced);
|
||||
SEQ_PUT_DEC(" kB\nAnonymous: ", mss->anonymous);
|
||||
SEQ_PUT_DEC(" kB\nLazyFree: ", mss->lazyfree);
|
||||
SEQ_PUT_DEC(" kB\nAnonHugePages: ", mss->anonymous_thp);
|
||||
SEQ_PUT_DEC(" kB\nShmemPmdMapped: ", mss->shmem_thp);
|
||||
SEQ_PUT_DEC(" kB\nShared_Hugetlb: ", mss->shared_hugetlb);
|
||||
seq_put_decimal_ull_width(m, " kB\nPrivate_Hugetlb: ",
|
||||
mss->private_hugetlb >> 10, 7);
|
||||
SEQ_PUT_DEC(" kB\nSwap: ", mss->swap);
|
||||
SEQ_PUT_DEC(" kB\nSwapPss: ",
|
||||
mss->swap_pss >> PSS_SHIFT);
|
||||
SEQ_PUT_DEC(" kB\nLocked: ",
|
||||
mss->pss_locked >> PSS_SHIFT);
|
||||
seq_puts(m, " kB\n");
|
||||
}
|
||||
|
||||
static int show_smap(struct seq_file *m, void *v)
|
||||
{
|
||||
struct vm_area_struct *vma = v;
|
||||
struct mem_size_stats mss;
|
||||
|
||||
memset(&mss, 0, sizeof(mss));
|
||||
|
||||
smap_gather_stats(vma, &mss);
|
||||
|
||||
show_map_vma(m, vma);
|
||||
|
||||
SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start);
|
||||
SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma));
|
||||
SEQ_PUT_DEC(" kB\nMMUPageSize: ", vma_mmu_pagesize(vma));
|
||||
seq_puts(m, " kB\n");
|
||||
|
||||
__show_smap(m, &mss);
|
||||
|
||||
if (arch_pkeys_enabled())
|
||||
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
|
||||
show_smap_vma_flags(m, vma);
|
||||
|
||||
if (!rollup_mode || last_vma) {
|
||||
SEQ_PUT_DEC("Rss: ", mss->resident);
|
||||
SEQ_PUT_DEC(" kB\nPss: ", mss->pss >> PSS_SHIFT);
|
||||
SEQ_PUT_DEC(" kB\nShared_Clean: ", mss->shared_clean);
|
||||
SEQ_PUT_DEC(" kB\nShared_Dirty: ", mss->shared_dirty);
|
||||
SEQ_PUT_DEC(" kB\nPrivate_Clean: ", mss->private_clean);
|
||||
SEQ_PUT_DEC(" kB\nPrivate_Dirty: ", mss->private_dirty);
|
||||
SEQ_PUT_DEC(" kB\nReferenced: ", mss->referenced);
|
||||
SEQ_PUT_DEC(" kB\nAnonymous: ", mss->anonymous);
|
||||
SEQ_PUT_DEC(" kB\nLazyFree: ", mss->lazyfree);
|
||||
SEQ_PUT_DEC(" kB\nAnonHugePages: ", mss->anonymous_thp);
|
||||
SEQ_PUT_DEC(" kB\nShmemPmdMapped: ", mss->shmem_thp);
|
||||
SEQ_PUT_DEC(" kB\nShared_Hugetlb: ", mss->shared_hugetlb);
|
||||
seq_put_decimal_ull_width(m, " kB\nPrivate_Hugetlb: ",
|
||||
mss->private_hugetlb >> 10, 7);
|
||||
SEQ_PUT_DEC(" kB\nSwap: ", mss->swap);
|
||||
SEQ_PUT_DEC(" kB\nSwapPss: ",
|
||||
mss->swap_pss >> PSS_SHIFT);
|
||||
SEQ_PUT_DEC(" kB\nLocked: ",
|
||||
mss->pss_locked >> PSS_SHIFT);
|
||||
seq_puts(m, " kB\n");
|
||||
}
|
||||
if (!rollup_mode) {
|
||||
if (arch_pkeys_enabled())
|
||||
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
|
||||
show_smap_vma_flags(m, vma);
|
||||
}
|
||||
m_cache_vma(m, vma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_smaps_rollup(struct seq_file *m, void *v)
|
||||
{
|
||||
struct proc_maps_private *priv = m->private;
|
||||
struct mem_size_stats mss;
|
||||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long last_vma_end = 0;
|
||||
int ret = 0;
|
||||
|
||||
priv->task = get_proc_task(priv->inode);
|
||||
if (!priv->task)
|
||||
return -ESRCH;
|
||||
|
||||
mm = priv->mm;
|
||||
if (!mm || !mmget_not_zero(mm)) {
|
||||
ret = -ESRCH;
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
memset(&mss, 0, sizeof(mss));
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
hold_task_mempolicy(priv);
|
||||
|
||||
for (vma = priv->mm->mmap; vma; vma = vma->vm_next) {
|
||||
smap_gather_stats(vma, &mss);
|
||||
last_vma_end = vma->vm_end;
|
||||
}
|
||||
|
||||
show_vma_header_prefix(m, priv->mm->mmap->vm_start,
|
||||
last_vma_end, 0, 0, 0, 0);
|
||||
seq_pad(m, ' ');
|
||||
seq_puts(m, "[rollup]\n");
|
||||
|
||||
__show_smap(m, &mss);
|
||||
|
||||
release_task_mempolicy(priv);
|
||||
up_read(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
|
||||
out_put_task:
|
||||
put_task_struct(priv->task);
|
||||
priv->task = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#undef SEQ_PUT_DEC
|
||||
|
||||
static int show_pid_smap(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_smap(m, v, 1);
|
||||
}
|
||||
|
||||
static int show_tid_smap(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_smap(m, v, 0);
|
||||
}
|
||||
|
||||
static const struct seq_operations proc_pid_smaps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_pid_smap
|
||||
};
|
||||
|
||||
static const struct seq_operations proc_tid_smaps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_tid_smap
|
||||
.show = show_smap
|
||||
};
|
||||
|
||||
static int pid_smaps_open(struct inode *inode, struct file *file)
|
||||
|
@ -874,28 +857,45 @@ static int pid_smaps_open(struct inode *inode, struct file *file)
|
|||
return do_maps_open(inode, file, &proc_pid_smaps_op);
|
||||
}
|
||||
|
||||
static int pid_smaps_rollup_open(struct inode *inode, struct file *file)
|
||||
static int smaps_rollup_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
int ret;
|
||||
struct proc_maps_private *priv;
|
||||
int ret = do_maps_open(inode, file, &proc_pid_smaps_op);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
seq = file->private_data;
|
||||
priv = seq->private;
|
||||
priv->rollup = kzalloc(sizeof(*priv->rollup), GFP_KERNEL);
|
||||
if (!priv->rollup) {
|
||||
proc_map_release(inode, file);
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL_ACCOUNT);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = single_open(file, show_smaps_rollup, priv);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
priv->inode = inode;
|
||||
priv->mm = proc_mem_open(inode, PTRACE_MODE_READ);
|
||||
if (IS_ERR(priv->mm)) {
|
||||
ret = PTR_ERR(priv->mm);
|
||||
|
||||
single_release(inode, file);
|
||||
goto out_free;
|
||||
}
|
||||
priv->rollup->first = true;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tid_smaps_open(struct inode *inode, struct file *file)
|
||||
static int smaps_rollup_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return do_maps_open(inode, file, &proc_tid_smaps_op);
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct proc_maps_private *priv = seq->private;
|
||||
|
||||
if (priv->mm)
|
||||
mmdrop(priv->mm);
|
||||
|
||||
kfree(priv);
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
const struct file_operations proc_pid_smaps_operations = {
|
||||
|
@ -906,17 +906,10 @@ const struct file_operations proc_pid_smaps_operations = {
|
|||
};
|
||||
|
||||
const struct file_operations proc_pid_smaps_rollup_operations = {
|
||||
.open = pid_smaps_rollup_open,
|
||||
.open = smaps_rollup_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = proc_map_release,
|
||||
};
|
||||
|
||||
const struct file_operations proc_tid_smaps_operations = {
|
||||
.open = tid_smaps_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = proc_map_release,
|
||||
.release = smaps_rollup_release,
|
||||
};
|
||||
|
||||
enum clear_refs_types {
|
||||
|
@ -1728,7 +1721,7 @@ static int gather_hugetlb_stats(pte_t *pte, unsigned long hmask,
|
|||
/*
|
||||
* Display pages allocated per node and memory policy via /proc.
|
||||
*/
|
||||
static int show_numa_map(struct seq_file *m, void *v, int is_pid)
|
||||
static int show_numa_map(struct seq_file *m, void *v)
|
||||
{
|
||||
struct numa_maps_private *numa_priv = m->private;
|
||||
struct proc_maps_private *proc_priv = &numa_priv->proc_maps;
|
||||
|
@ -1812,45 +1805,17 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int show_pid_numa_map(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_numa_map(m, v, 1);
|
||||
}
|
||||
|
||||
static int show_tid_numa_map(struct seq_file *m, void *v)
|
||||
{
|
||||
return show_numa_map(m, v, 0);
|
||||
}
|
||||
|
||||
static const struct seq_operations proc_pid_numa_maps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_pid_numa_map,
|
||||
.show = show_numa_map,
|
||||
};
|
||||
|
||||
static const struct seq_operations proc_tid_numa_maps_op = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_tid_numa_map,
|
||||
};
|
||||
|
||||
static int numa_maps_open(struct inode *inode, struct file *file,
|
||||
const struct seq_operations *ops)
|
||||
{
|
||||
return proc_maps_open(inode, file, ops,
|
||||
sizeof(struct numa_maps_private));
|
||||
}
|
||||
|
||||
static int pid_numa_maps_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return numa_maps_open(inode, file, &proc_pid_numa_maps_op);
|
||||
}
|
||||
|
||||
static int tid_numa_maps_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return numa_maps_open(inode, file, &proc_tid_numa_maps_op);
|
||||
return proc_maps_open(inode, file, &proc_pid_numa_maps_op,
|
||||
sizeof(struct numa_maps_private));
|
||||
}
|
||||
|
||||
const struct file_operations proc_pid_numa_maps_operations = {
|
||||
|
@ -1860,10 +1825,4 @@ const struct file_operations proc_pid_numa_maps_operations = {
|
|||
.release = proc_map_release,
|
||||
};
|
||||
|
||||
const struct file_operations proc_tid_numa_maps_operations = {
|
||||
.open = tid_numa_maps_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = proc_map_release,
|
||||
};
|
||||
#endif /* CONFIG_NUMA */
|
||||
|
|
|
@ -142,8 +142,7 @@ static int is_stack(struct vm_area_struct *vma)
|
|||
/*
|
||||
* display a single VMA to a sequenced file
|
||||
*/
|
||||
static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
|
||||
int is_pid)
|
||||
static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
unsigned long ino = 0;
|
||||
|
@ -189,22 +188,11 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
|
|||
/*
|
||||
* display mapping lines for a particular process's /proc/pid/maps
|
||||
*/
|
||||
static int show_map(struct seq_file *m, void *_p, int is_pid)
|
||||
static int show_map(struct seq_file *m, void *_p)
|
||||
{
|
||||
struct rb_node *p = _p;
|
||||
|
||||
return nommu_vma_show(m, rb_entry(p, struct vm_area_struct, vm_rb),
|
||||
is_pid);
|
||||
}
|
||||
|
||||
static int show_pid_map(struct seq_file *m, void *_p)
|
||||
{
|
||||
return show_map(m, _p, 1);
|
||||
}
|
||||
|
||||
static int show_tid_map(struct seq_file *m, void *_p)
|
||||
{
|
||||
return show_map(m, _p, 0);
|
||||
return nommu_vma_show(m, rb_entry(p, struct vm_area_struct, vm_rb));
|
||||
}
|
||||
|
||||
static void *m_start(struct seq_file *m, loff_t *pos)
|
||||
|
@ -260,14 +248,7 @@ static const struct seq_operations proc_pid_maps_ops = {
|
|||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_pid_map
|
||||
};
|
||||
|
||||
static const struct seq_operations proc_tid_maps_ops = {
|
||||
.start = m_start,
|
||||
.next = m_next,
|
||||
.stop = m_stop,
|
||||
.show = show_tid_map
|
||||
.show = show_map
|
||||
};
|
||||
|
||||
static int maps_open(struct inode *inode, struct file *file,
|
||||
|
@ -308,11 +289,6 @@ static int pid_maps_open(struct inode *inode, struct file *file)
|
|||
return maps_open(inode, file, &proc_pid_maps_ops);
|
||||
}
|
||||
|
||||
static int tid_maps_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return maps_open(inode, file, &proc_tid_maps_ops);
|
||||
}
|
||||
|
||||
const struct file_operations proc_pid_maps_operations = {
|
||||
.open = pid_maps_open,
|
||||
.read = seq_read,
|
||||
|
@ -320,10 +296,3 @@ const struct file_operations proc_pid_maps_operations = {
|
|||
.release = map_release,
|
||||
};
|
||||
|
||||
const struct file_operations proc_tid_maps_operations = {
|
||||
.open = tid_maps_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = map_release,
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
static int uptime_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct timespec uptime;
|
||||
struct timespec64 uptime;
|
||||
struct timespec64 idle;
|
||||
u64 nsec;
|
||||
u32 rem;
|
||||
|
@ -19,7 +19,7 @@ static int uptime_proc_show(struct seq_file *m, void *v)
|
|||
for_each_possible_cpu(i)
|
||||
nsec += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
|
||||
|
||||
get_monotonic_boottime(&uptime);
|
||||
ktime_get_boottime_ts64(&uptime);
|
||||
idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
|
||||
idle.tv_nsec = rem;
|
||||
seq_printf(m, "%lu.%02lu %lu.%02lu\n",
|
||||
|
|
|
@ -379,7 +379,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer,
|
|||
* On s390 the fault handler is used for memory regions that can't be mapped
|
||||
* directly with remap_pfn_range().
|
||||
*/
|
||||
static int mmap_vmcore_fault(struct vm_fault *vmf)
|
||||
static vm_fault_t mmap_vmcore_fault(struct vm_fault *vmf)
|
||||
{
|
||||
#ifdef CONFIG_S390
|
||||
struct address_space *mapping = vmf->vma->vm_file->f_mapping;
|
||||
|
|
|
@ -33,30 +33,22 @@ static int sd_is_left_mergeable(struct reiserfs_key *key, unsigned long bsize)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static char *print_time(time_t t)
|
||||
{
|
||||
static char timebuf[256];
|
||||
|
||||
sprintf(timebuf, "%ld", t);
|
||||
return timebuf;
|
||||
}
|
||||
|
||||
static void sd_print_item(struct item_head *ih, char *item)
|
||||
{
|
||||
printk("\tmode | size | nlinks | first direct | mtime\n");
|
||||
if (stat_data_v1(ih)) {
|
||||
struct stat_data_v1 *sd = (struct stat_data_v1 *)item;
|
||||
|
||||
printk("\t0%-6o | %6u | %2u | %d | %s\n", sd_v1_mode(sd),
|
||||
printk("\t0%-6o | %6u | %2u | %d | %u\n", sd_v1_mode(sd),
|
||||
sd_v1_size(sd), sd_v1_nlink(sd),
|
||||
sd_v1_first_direct_byte(sd),
|
||||
print_time(sd_v1_mtime(sd)));
|
||||
sd_v1_mtime(sd));
|
||||
} else {
|
||||
struct stat_data *sd = (struct stat_data *)item;
|
||||
|
||||
printk("\t0%-6o | %6llu | %2u | %d | %s\n", sd_v2_mode(sd),
|
||||
printk("\t0%-6o | %6llu | %2u | %d | %u\n", sd_v2_mode(sd),
|
||||
(unsigned long long)sd_v2_size(sd), sd_v2_nlink(sd),
|
||||
sd_v2_rdev(sd), print_time(sd_v2_mtime(sd)));
|
||||
sd_v2_rdev(sd), sd_v2_mtime(sd));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2381,7 +2381,7 @@ static int journal_read(struct super_block *sb)
|
|||
struct reiserfs_journal_desc *desc;
|
||||
unsigned int oldest_trans_id = 0;
|
||||
unsigned int oldest_invalid_trans_id = 0;
|
||||
time_t start;
|
||||
time64_t start;
|
||||
unsigned long oldest_start = 0;
|
||||
unsigned long cur_dblock = 0;
|
||||
unsigned long newest_mount_id = 9;
|
||||
|
@ -2395,7 +2395,7 @@ static int journal_read(struct super_block *sb)
|
|||
cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(sb);
|
||||
reiserfs_info(sb, "checking transaction log (%pg)\n",
|
||||
journal->j_dev_bd);
|
||||
start = get_seconds();
|
||||
start = ktime_get_seconds();
|
||||
|
||||
/*
|
||||
* step 1, read in the journal header block. Check the transaction
|
||||
|
@ -2556,7 +2556,7 @@ static int journal_read(struct super_block *sb)
|
|||
if (replay_count > 0) {
|
||||
reiserfs_info(sb,
|
||||
"replayed %d transactions in %lu seconds\n",
|
||||
replay_count, get_seconds() - start);
|
||||
replay_count, ktime_get_seconds() - start);
|
||||
}
|
||||
/* needed to satisfy the locking in _update_journal_header_block */
|
||||
reiserfs_write_lock(sb);
|
||||
|
@ -2914,7 +2914,7 @@ int journal_transaction_should_end(struct reiserfs_transaction_handle *th,
|
|||
int new_alloc)
|
||||
{
|
||||
struct reiserfs_journal *journal = SB_JOURNAL(th->t_super);
|
||||
time_t now = get_seconds();
|
||||
time64_t now = ktime_get_seconds();
|
||||
/* cannot restart while nested */
|
||||
BUG_ON(!th->t_trans_id);
|
||||
if (th->t_refcount > 1)
|
||||
|
@ -3023,7 +3023,7 @@ static int do_journal_begin_r(struct reiserfs_transaction_handle *th,
|
|||
struct super_block *sb, unsigned long nblocks,
|
||||
int join)
|
||||
{
|
||||
time_t now = get_seconds();
|
||||
time64_t now = ktime_get_seconds();
|
||||
unsigned int old_trans_id;
|
||||
struct reiserfs_journal *journal = SB_JOURNAL(sb);
|
||||
struct reiserfs_transaction_handle myth;
|
||||
|
@ -3056,7 +3056,7 @@ static int do_journal_begin_r(struct reiserfs_transaction_handle *th,
|
|||
PROC_INFO_INC(sb, journal.journal_relock_writers);
|
||||
goto relock;
|
||||
}
|
||||
now = get_seconds();
|
||||
now = ktime_get_seconds();
|
||||
|
||||
/*
|
||||
* if there is no room in the journal OR
|
||||
|
@ -3119,7 +3119,7 @@ static int do_journal_begin_r(struct reiserfs_transaction_handle *th,
|
|||
}
|
||||
/* we are the first writer, set trans_id */
|
||||
if (journal->j_trans_start_time == 0) {
|
||||
journal->j_trans_start_time = get_seconds();
|
||||
journal->j_trans_start_time = ktime_get_seconds();
|
||||
}
|
||||
atomic_inc(&journal->j_wcount);
|
||||
journal->j_len_alloc += nblocks;
|
||||
|
@ -3559,11 +3559,11 @@ static void flush_async_commits(struct work_struct *work)
|
|||
*/
|
||||
void reiserfs_flush_old_commits(struct super_block *sb)
|
||||
{
|
||||
time_t now;
|
||||
time64_t now;
|
||||
struct reiserfs_transaction_handle th;
|
||||
struct reiserfs_journal *journal = SB_JOURNAL(sb);
|
||||
|
||||
now = get_seconds();
|
||||
now = ktime_get_seconds();
|
||||
/*
|
||||
* safety check so we don't flush while we are replaying the log during
|
||||
* mount
|
||||
|
@ -3613,7 +3613,7 @@ void reiserfs_flush_old_commits(struct super_block *sb)
|
|||
static int check_journal_end(struct reiserfs_transaction_handle *th, int flags)
|
||||
{
|
||||
|
||||
time_t now;
|
||||
time64_t now;
|
||||
int flush = flags & FLUSH_ALL;
|
||||
int commit_now = flags & COMMIT_NOW;
|
||||
int wait_on_commit = flags & WAIT;
|
||||
|
@ -3694,7 +3694,7 @@ static int check_journal_end(struct reiserfs_transaction_handle *th, int flags)
|
|||
}
|
||||
|
||||
/* deal with old transactions where we are the last writers */
|
||||
now = get_seconds();
|
||||
now = ktime_get_seconds();
|
||||
if ((now - journal->j_trans_start_time) > journal->j_max_trans_age) {
|
||||
commit_now = 1;
|
||||
journal->j_next_async_flush = 1;
|
||||
|
|
|
@ -297,6 +297,13 @@ static int show_oidmap(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static time64_t ktime_mono_to_real_seconds(time64_t mono)
|
||||
{
|
||||
ktime_t kt = ktime_set(mono, NSEC_PER_SEC/2);
|
||||
|
||||
return ktime_divns(ktime_mono_to_real(kt), NSEC_PER_SEC);
|
||||
}
|
||||
|
||||
static int show_journal(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct super_block *sb = m->private;
|
||||
|
@ -325,7 +332,7 @@ static int show_journal(struct seq_file *m, void *unused)
|
|||
"j_bcount: \t%lu\n"
|
||||
"j_first_unflushed_offset: \t%lu\n"
|
||||
"j_last_flush_trans_id: \t%u\n"
|
||||
"j_trans_start_time: \t%li\n"
|
||||
"j_trans_start_time: \t%lli\n"
|
||||
"j_list_bitmap_index: \t%i\n"
|
||||
"j_must_wait: \t%i\n"
|
||||
"j_next_full_flush: \t%i\n"
|
||||
|
@ -366,7 +373,7 @@ static int show_journal(struct seq_file *m, void *unused)
|
|||
JF(j_bcount),
|
||||
JF(j_first_unflushed_offset),
|
||||
JF(j_last_flush_trans_id),
|
||||
JF(j_trans_start_time),
|
||||
ktime_mono_to_real_seconds(JF(j_trans_start_time)),
|
||||
JF(j_list_bitmap_index),
|
||||
JF(j_must_wait),
|
||||
JF(j_next_full_flush),
|
||||
|
|
|
@ -271,7 +271,7 @@ struct reiserfs_journal_list {
|
|||
|
||||
struct mutex j_commit_mutex;
|
||||
unsigned int j_trans_id;
|
||||
time_t j_timestamp;
|
||||
time64_t j_timestamp; /* write-only but useful for crash dump analysis */
|
||||
struct reiserfs_list_bitmap *j_list_bitmap;
|
||||
struct buffer_head *j_commit_bh; /* commit buffer head */
|
||||
struct reiserfs_journal_cnode *j_realblock;
|
||||
|
@ -331,7 +331,7 @@ struct reiserfs_journal {
|
|||
|
||||
struct buffer_head *j_header_bh;
|
||||
|
||||
time_t j_trans_start_time; /* time this transaction started */
|
||||
time64_t j_trans_start_time; /* time this transaction started */
|
||||
struct mutex j_mutex;
|
||||
struct mutex j_flush_mutex;
|
||||
|
||||
|
|
|
@ -792,8 +792,10 @@ static int listxattr_filler(struct dir_context *ctx, const char *name,
|
|||
return 0;
|
||||
size = namelen + 1;
|
||||
if (b->buf) {
|
||||
if (size > b->size)
|
||||
if (b->pos + size > b->size) {
|
||||
b->pos = -ERANGE;
|
||||
return -ERANGE;
|
||||
}
|
||||
memcpy(b->buf + b->pos, name, namelen);
|
||||
b->buf[b->pos + namelen] = 0;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
static int sysv_sync_fs(struct super_block *sb, int wait)
|
||||
{
|
||||
struct sysv_sb_info *sbi = SYSV_SB(sb);
|
||||
unsigned long time = get_seconds(), old_time;
|
||||
u32 time = (u32)ktime_get_real_seconds(), old_time;
|
||||
|
||||
mutex_lock(&sbi->s_lock);
|
||||
|
||||
|
@ -46,8 +46,8 @@ static int sysv_sync_fs(struct super_block *sb, int wait)
|
|||
*/
|
||||
old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
|
||||
if (sbi->s_type == FSTYPE_SYSV4) {
|
||||
if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
|
||||
*sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38 - time);
|
||||
if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38u - old_time))
|
||||
*sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38u - time);
|
||||
*sbi->s_sb_time = cpu_to_fs32(sbi, time);
|
||||
mark_buffer_dirty(sbi->s_bh2);
|
||||
}
|
||||
|
|
|
@ -910,7 +910,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
|
|||
*/
|
||||
spin_lock(&ctx->fault_pending_wqh.lock);
|
||||
__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &range);
|
||||
__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, &range);
|
||||
__wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, &range);
|
||||
spin_unlock(&ctx->fault_pending_wqh.lock);
|
||||
|
||||
/* Flush pending events that may still wait on event_wqh */
|
||||
|
@ -1066,7 +1066,7 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
|
|||
* anyway.
|
||||
*/
|
||||
list_del(&uwq->wq.entry);
|
||||
__add_wait_queue(&ctx->fault_wqh, &uwq->wq);
|
||||
add_wait_queue(&ctx->fault_wqh, &uwq->wq);
|
||||
|
||||
write_seqcount_end(&ctx->refile_seq);
|
||||
|
||||
|
@ -1215,7 +1215,7 @@ static void __wake_userfault(struct userfaultfd_ctx *ctx,
|
|||
__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL,
|
||||
range);
|
||||
if (waitqueue_active(&ctx->fault_wqh))
|
||||
__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, range);
|
||||
__wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, range);
|
||||
spin_unlock(&ctx->fault_pending_wqh.lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,9 +75,19 @@ struct bug_entry {
|
|||
|
||||
/*
|
||||
* WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report
|
||||
* significant issues that need prompt attention if they should ever
|
||||
* appear at runtime. Use the versions with printk format strings
|
||||
* to provide better diagnostics.
|
||||
* significant kernel issues that need prompt attention if they should ever
|
||||
* appear at runtime.
|
||||
*
|
||||
* Do not use these macros when checking for invalid external inputs
|
||||
* (e.g. invalid system call arguments, or invalid data coming from
|
||||
* network/devices), and on transient conditions like ENOMEM or EAGAIN.
|
||||
* These macros should be used for recoverable kernel issues only.
|
||||
* For invalid external inputs, transient conditions, etc use
|
||||
* pr_err[_once/_ratelimited]() followed by dump_stack(), if necessary.
|
||||
* Do not include "BUG"/"WARNING" in format strings manually to make these
|
||||
* conditions distinguishable from kernel issues.
|
||||
*
|
||||
* Use the versions with printk format strings to provide better diagnostics.
|
||||
*/
|
||||
#ifndef __WARN_TAINT
|
||||
extern __printf(3, 4)
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
#define KSYM_FUNC(x) x
|
||||
#endif
|
||||
#ifdef CONFIG_64BIT
|
||||
#define __put .quad
|
||||
#ifndef KSYM_ALIGN
|
||||
#define KSYM_ALIGN 8
|
||||
#endif
|
||||
#else
|
||||
#define __put .long
|
||||
#ifndef KSYM_ALIGN
|
||||
#define KSYM_ALIGN 4
|
||||
#endif
|
||||
|
@ -19,6 +17,16 @@
|
|||
#define KCRC_ALIGN 4
|
||||
#endif
|
||||
|
||||
.macro __put, val, name
|
||||
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
|
||||
.long \val - ., \name - .
|
||||
#elif defined(CONFIG_64BIT)
|
||||
.quad \val, \name
|
||||
#else
|
||||
.long \val, \name
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/*
|
||||
* note on .section use: @progbits vs %progbits nastiness doesn't matter,
|
||||
* since we immediately emit into those sections anyway.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
struct page;
|
||||
struct device;
|
||||
|
@ -75,7 +76,7 @@ enum wb_reason {
|
|||
*/
|
||||
struct bdi_writeback_congested {
|
||||
unsigned long state; /* WB_[a]sync_congested flags */
|
||||
atomic_t refcnt; /* nr of attached wb's and blkg */
|
||||
refcount_t refcnt; /* nr of attached wb's and blkg */
|
||||
|
||||
#ifdef CONFIG_CGROUP_WRITEBACK
|
||||
struct backing_dev_info *__bdi; /* the associated bdi, set to NULL
|
||||
|
|
|
@ -404,13 +404,13 @@ static inline bool inode_cgwb_enabled(struct inode *inode)
|
|||
static inline struct bdi_writeback_congested *
|
||||
wb_congested_get_create(struct backing_dev_info *bdi, int blkcg_id, gfp_t gfp)
|
||||
{
|
||||
atomic_inc(&bdi->wb_congested->refcnt);
|
||||
refcount_inc(&bdi->wb_congested->refcnt);
|
||||
return bdi->wb_congested;
|
||||
}
|
||||
|
||||
static inline void wb_congested_put(struct bdi_writeback_congested *congested)
|
||||
{
|
||||
if (atomic_dec_and_test(&congested->refcnt))
|
||||
if (refcount_dec_and_test(&congested->refcnt))
|
||||
kfree(congested);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
#include <asm/types.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
|
||||
#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE)
|
||||
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(long))
|
||||
|
||||
extern unsigned int __sw_hweight8(unsigned int w);
|
||||
extern unsigned int __sw_hweight16(unsigned int w);
|
||||
|
|
|
@ -280,6 +280,25 @@ unsigned long read_word_at_a_time(const void *addr)
|
|||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/*
|
||||
* Force the compiler to emit 'sym' as a symbol, so that we can reference
|
||||
* it from inline assembler. Necessary in case 'sym' could be inlined
|
||||
* otherwise, or eliminated entirely due to lack of references that are
|
||||
* visible to the compiler.
|
||||
*/
|
||||
#define __ADDRESSABLE(sym) \
|
||||
static void * __attribute__((section(".discard.addressable"), used)) \
|
||||
__PASTE(__addressable_##sym, __LINE__) = (void *)&sym;
|
||||
|
||||
/**
|
||||
* offset_to_ptr - convert a relative memory offset to an absolute pointer
|
||||
* @off: the address of the 32-bit offset value
|
||||
*/
|
||||
static inline void *offset_to_ptr(const int *off)
|
||||
{
|
||||
return (void *)((unsigned long)off + *off);
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#ifndef __optimize
|
||||
|
@ -313,7 +332,7 @@ unsigned long read_word_at_a_time(const void *addr)
|
|||
#ifdef __OPTIMIZE__
|
||||
# define __compiletime_assert(condition, msg, prefix, suffix) \
|
||||
do { \
|
||||
bool __cond = !(condition); \
|
||||
int __cond = !(condition); \
|
||||
extern void prefix ## suffix(void) __compiletime_error(msg); \
|
||||
if (__cond) \
|
||||
prefix ## suffix(); \
|
||||
|
|
|
@ -60,6 +60,8 @@ phys_addr_t paddr_vmcoreinfo_note(void);
|
|||
#define VMCOREINFO_CONFIG(name) \
|
||||
vmcoreinfo_append_str("CONFIG_%s=y\n", #name)
|
||||
|
||||
extern unsigned char *vmcoreinfo_data;
|
||||
extern size_t vmcoreinfo_size;
|
||||
extern u32 *vmcoreinfo_note;
|
||||
|
||||
Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type,
|
||||
|
|
11
include/linux/crc64.h
Normal file
11
include/linux/crc64.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* See lib/crc64.c for the related specification and polynomial arithmetic.
|
||||
*/
|
||||
#ifndef _LINUX_CRC64_H
|
||||
#define _LINUX_CRC64_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
u64 __pure crc64_be(u64 crc, const void *p, size_t len);
|
||||
#endif /* _LINUX_CRC64_H */
|
|
@ -18,12 +18,6 @@
|
|||
#define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
struct kernel_symbol
|
||||
{
|
||||
unsigned long value;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern struct module __this_module;
|
||||
#define THIS_MODULE (&__this_module)
|
||||
|
@ -54,19 +48,58 @@ extern struct module __this_module;
|
|||
#define __CRC_SYMBOL(sym, sec)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
|
||||
#include <linux/compiler.h>
|
||||
/*
|
||||
* Emit the ksymtab entry as a pair of relative references: this reduces
|
||||
* the size by half on 64-bit architectures, and eliminates the need for
|
||||
* absolute relocations that require runtime processing on relocatable
|
||||
* kernels.
|
||||
*/
|
||||
#define __KSYMTAB_ENTRY(sym, sec) \
|
||||
__ADDRESSABLE(sym) \
|
||||
asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \
|
||||
" .balign 8 \n" \
|
||||
"__ksymtab_" #sym ": \n" \
|
||||
" .long " #sym "- . \n" \
|
||||
" .long __kstrtab_" #sym "- . \n" \
|
||||
" .previous \n")
|
||||
|
||||
struct kernel_symbol {
|
||||
int value_offset;
|
||||
int name_offset;
|
||||
};
|
||||
#else
|
||||
#define __KSYMTAB_ENTRY(sym, sec) \
|
||||
static const struct kernel_symbol __ksymtab_##sym \
|
||||
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
|
||||
= { (unsigned long)&sym, __kstrtab_##sym }
|
||||
|
||||
struct kernel_symbol {
|
||||
unsigned long value;
|
||||
const char *name;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* For every exported symbol, place a struct in the __ksymtab section */
|
||||
#define ___EXPORT_SYMBOL(sym, sec) \
|
||||
extern typeof(sym) sym; \
|
||||
__CRC_SYMBOL(sym, sec) \
|
||||
static const char __kstrtab_##sym[] \
|
||||
__attribute__((section("__ksymtab_strings"), aligned(1))) \
|
||||
__attribute__((section("__ksymtab_strings"), used, aligned(1))) \
|
||||
= #sym; \
|
||||
static const struct kernel_symbol __ksymtab_##sym \
|
||||
__used \
|
||||
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
|
||||
= { (unsigned long)&sym, __kstrtab_##sym }
|
||||
__KSYMTAB_ENTRY(sym, sec)
|
||||
|
||||
#if defined(__KSYM_DEPS__)
|
||||
#if defined(__DISABLE_EXPORTS)
|
||||
|
||||
/*
|
||||
* Allow symbol exports to be disabled completely so that C code may
|
||||
* be reused in other execution contexts such as the UEFI stub or the
|
||||
* decompressor.
|
||||
*/
|
||||
#define __EXPORT_SYMBOL(sym, sec)
|
||||
|
||||
#elif defined(__KSYM_DEPS__)
|
||||
|
||||
/*
|
||||
* For fine grained build dependencies, we want to tell the build system
|
||||
|
|
|
@ -116,8 +116,24 @@
|
|||
typedef int (*initcall_t)(void);
|
||||
typedef void (*exitcall_t)(void);
|
||||
|
||||
extern initcall_t __con_initcall_start[], __con_initcall_end[];
|
||||
extern initcall_t __security_initcall_start[], __security_initcall_end[];
|
||||
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
|
||||
typedef int initcall_entry_t;
|
||||
|
||||
static inline initcall_t initcall_from_entry(initcall_entry_t *entry)
|
||||
{
|
||||
return offset_to_ptr(entry);
|
||||
}
|
||||
#else
|
||||
typedef initcall_t initcall_entry_t;
|
||||
|
||||
static inline initcall_t initcall_from_entry(initcall_entry_t *entry)
|
||||
{
|
||||
return *entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern initcall_entry_t __con_initcall_start[], __con_initcall_end[];
|
||||
extern initcall_entry_t __security_initcall_start[], __security_initcall_end[];
|
||||
|
||||
/* Used for contructor calls. */
|
||||
typedef void (*ctor_fn_t)(void);
|
||||
|
@ -167,9 +183,20 @@ extern bool initcall_debug;
|
|||
* as KEEP() in the linker script.
|
||||
*/
|
||||
|
||||
#define __define_initcall(fn, id) \
|
||||
#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
|
||||
#define ___define_initcall(fn, id, __sec) \
|
||||
__ADDRESSABLE(fn) \
|
||||
asm(".section \"" #__sec ".init\", \"a\" \n" \
|
||||
"__initcall_" #fn #id ": \n" \
|
||||
".long " #fn " - . \n" \
|
||||
".previous \n");
|
||||
#else
|
||||
#define ___define_initcall(fn, id, __sec) \
|
||||
static initcall_t __initcall_##fn##id __used \
|
||||
__attribute__((__section__(".initcall" #id ".init"))) = fn;
|
||||
__attribute__((__section__(#__sec ".init"))) = fn;
|
||||
#endif
|
||||
|
||||
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
|
||||
|
||||
/*
|
||||
* Early initcalls run before initializing SMP.
|
||||
|
@ -208,13 +235,8 @@ extern bool initcall_debug;
|
|||
#define __exitcall(fn) \
|
||||
static exitcall_t __exitcall_##fn __exit_call = fn
|
||||
|
||||
#define console_initcall(fn) \
|
||||
static initcall_t __initcall_##fn \
|
||||
__used __section(.con_initcall.init) = fn
|
||||
|
||||
#define security_initcall(fn) \
|
||||
static initcall_t __initcall_##fn \
|
||||
__used __section(.security_initcall.init) = fn
|
||||
#define console_initcall(fn) ___define_initcall(fn,, .con_initcall)
|
||||
#define security_initcall(fn) ___define_initcall(fn,, .security_initcall)
|
||||
|
||||
struct obs_kernel_param {
|
||||
const char *str;
|
||||
|
|
|
@ -16,10 +16,9 @@ struct user_namespace;
|
|||
struct ipc_ids {
|
||||
int in_use;
|
||||
unsigned short seq;
|
||||
bool tables_initialized;
|
||||
struct rw_semaphore rwsem;
|
||||
struct idr ipcs_idr;
|
||||
int max_id;
|
||||
int max_idx;
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
int next_id;
|
||||
#endif
|
||||
|
|
|
@ -35,7 +35,7 @@ struct vmcoredd_node {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_PROC_KCORE
|
||||
extern void kclist_add(struct kcore_list *, void *, size_t, int type);
|
||||
void __init kclist_add(struct kcore_list *, void *, size_t, int type);
|
||||
#else
|
||||
static inline
|
||||
void kclist_add(struct kcore_list *new, void *addr, size_t size, int type)
|
||||
|
|
|
@ -85,7 +85,23 @@
|
|||
* arguments just once each.
|
||||
*/
|
||||
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
|
||||
/**
|
||||
* round_up - round up to next specified power of 2
|
||||
* @x: the value to round
|
||||
* @y: multiple to round up to (must be a power of 2)
|
||||
*
|
||||
* Rounds @x up to next multiple of @y (which must be a power of 2).
|
||||
* To perform arbitrary rounding up, use roundup() below.
|
||||
*/
|
||||
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
|
||||
/**
|
||||
* round_down - round down to next specified power of 2
|
||||
* @x: the value to round
|
||||
* @y: multiple to round down to (must be a power of 2)
|
||||
*
|
||||
* Rounds @x down to next multiple of @y (which must be a power of 2).
|
||||
* To perform arbitrary rounding down, use rounddown() below.
|
||||
*/
|
||||
#define round_down(x, y) ((x) & ~__round_mask(x, y))
|
||||
|
||||
/**
|
||||
|
@ -110,13 +126,30 @@
|
|||
# define DIV_ROUND_UP_SECTOR_T(ll,d) DIV_ROUND_UP(ll,d)
|
||||
#endif
|
||||
|
||||
/* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */
|
||||
/**
|
||||
* roundup - round up to the next specified multiple
|
||||
* @x: the value to up
|
||||
* @y: multiple to round up to
|
||||
*
|
||||
* Rounds @x up to next multiple of @y. If @y will always be a power
|
||||
* of 2, consider using the faster round_up().
|
||||
*
|
||||
* The `const' here prevents gcc-3.3 from calling __divdi3
|
||||
*/
|
||||
#define roundup(x, y) ( \
|
||||
{ \
|
||||
const typeof(y) __y = y; \
|
||||
(((x) + (__y - 1)) / __y) * __y; \
|
||||
} \
|
||||
)
|
||||
/**
|
||||
* rounddown - round down to next specified multiple
|
||||
* @x: the value to round
|
||||
* @y: multiple to round down to
|
||||
*
|
||||
* Rounds @x down to next multiple of @y. If @y will always be a power
|
||||
* of 2, consider using the faster round_down().
|
||||
*/
|
||||
#define rounddown(x, y) ( \
|
||||
{ \
|
||||
typeof(x) __x = (x); \
|
||||
|
|
|
@ -1289,8 +1289,8 @@ static inline long kvm_arch_vcpu_async_ioctl(struct file *filp,
|
|||
}
|
||||
#endif /* CONFIG_HAVE_KVM_VCPU_ASYNC_IOCTL */
|
||||
|
||||
void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
|
||||
unsigned long start, unsigned long end);
|
||||
int kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
|
||||
unsigned long start, unsigned long end, bool blockable);
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE
|
||||
int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu);
|
||||
|
|
|
@ -225,6 +225,11 @@ struct mem_cgroup {
|
|||
*/
|
||||
bool use_hierarchy;
|
||||
|
||||
/*
|
||||
* Should the OOM killer kill all belonging tasks, had it kill one?
|
||||
*/
|
||||
bool oom_group;
|
||||
|
||||
/* protected by memcg_oom_lock */
|
||||
bool oom_lock;
|
||||
int under_oom;
|
||||
|
@ -542,6 +547,9 @@ static inline bool task_in_memcg_oom(struct task_struct *p)
|
|||
}
|
||||
|
||||
bool mem_cgroup_oom_synchronize(bool wait);
|
||||
struct mem_cgroup *mem_cgroup_get_oom_group(struct task_struct *victim,
|
||||
struct mem_cgroup *oom_domain);
|
||||
void mem_cgroup_print_oom_group(struct mem_cgroup *memcg);
|
||||
|
||||
#ifdef CONFIG_MEMCG_SWAP
|
||||
extern int do_swap_account;
|
||||
|
@ -1001,6 +1009,16 @@ static inline bool mem_cgroup_oom_synchronize(bool wait)
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline struct mem_cgroup *mem_cgroup_get_oom_group(
|
||||
struct task_struct *victim, struct mem_cgroup *oom_domain)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline unsigned long memcg_page_state(struct mem_cgroup *memcg,
|
||||
int idx)
|
||||
{
|
||||
|
|
|
@ -319,6 +319,7 @@ static inline int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
|
|||
static inline void remove_memory(int nid, u64 start, u64 size) {}
|
||||
#endif /* CONFIG_MEMORY_HOTREMOVE */
|
||||
|
||||
extern void __ref free_area_init_core_hotplug(int nid);
|
||||
extern int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
|
||||
void *arg, int (*func)(struct memory_block *, void *));
|
||||
extern int add_memory(int nid, u64 start, u64 size);
|
||||
|
|
|
@ -456,6 +456,7 @@ static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
|
|||
{
|
||||
static const struct vm_operations_struct dummy_vm_ops = {};
|
||||
|
||||
memset(vma, 0, sizeof(*vma));
|
||||
vma->vm_mm = mm;
|
||||
vma->vm_ops = &dummy_vm_ops;
|
||||
INIT_LIST_HEAD(&vma->anon_vma_chain);
|
||||
|
@ -959,15 +960,6 @@ static inline int page_zone_id(struct page *page)
|
|||
return (page->flags >> ZONEID_PGSHIFT) & ZONEID_MASK;
|
||||
}
|
||||
|
||||
static inline int zone_to_nid(struct zone *zone)
|
||||
{
|
||||
#ifdef CONFIG_NUMA
|
||||
return zone->node;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NODE_NOT_IN_PAGE_FLAGS
|
||||
extern int page_to_nid(const struct page *page);
|
||||
#else
|
||||
|
@ -2023,7 +2015,7 @@ static inline spinlock_t *pud_lock(struct mm_struct *mm, pud_t *pud)
|
|||
|
||||
extern void __init pagecache_init(void);
|
||||
extern void free_area_init(unsigned long * zones_size);
|
||||
extern void free_area_init_node(int nid, unsigned long * zones_size,
|
||||
extern void __init free_area_init_node(int nid, unsigned long * zones_size,
|
||||
unsigned long zone_start_pfn, unsigned long *zholes_size);
|
||||
extern void free_initmem(void);
|
||||
|
||||
|
|
|
@ -151,13 +151,15 @@ struct mmu_notifier_ops {
|
|||
* address space but may still be referenced by sptes until
|
||||
* the last refcount is dropped.
|
||||
*
|
||||
* If both of these callbacks cannot block, and invalidate_range
|
||||
* cannot block, mmu_notifier_ops.flags should have
|
||||
* MMU_INVALIDATE_DOES_NOT_BLOCK set.
|
||||
* If blockable argument is set to false then the callback cannot
|
||||
* sleep and has to return with -EAGAIN. 0 should be returned
|
||||
* otherwise.
|
||||
*
|
||||
*/
|
||||
void (*invalidate_range_start)(struct mmu_notifier *mn,
|
||||
int (*invalidate_range_start)(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end);
|
||||
unsigned long start, unsigned long end,
|
||||
bool blockable);
|
||||
void (*invalidate_range_end)(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end);
|
||||
|
@ -229,8 +231,9 @@ extern int __mmu_notifier_test_young(struct mm_struct *mm,
|
|||
unsigned long address);
|
||||
extern void __mmu_notifier_change_pte(struct mm_struct *mm,
|
||||
unsigned long address, pte_t pte);
|
||||
extern void __mmu_notifier_invalidate_range_start(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end);
|
||||
extern int __mmu_notifier_invalidate_range_start(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end,
|
||||
bool blockable);
|
||||
extern void __mmu_notifier_invalidate_range_end(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end,
|
||||
bool only_end);
|
||||
|
@ -281,7 +284,15 @@ static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm,
|
|||
unsigned long start, unsigned long end)
|
||||
{
|
||||
if (mm_has_notifiers(mm))
|
||||
__mmu_notifier_invalidate_range_start(mm, start, end);
|
||||
__mmu_notifier_invalidate_range_start(mm, start, end, true);
|
||||
}
|
||||
|
||||
static inline int mmu_notifier_invalidate_range_start_nonblock(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
if (mm_has_notifiers(mm))
|
||||
return __mmu_notifier_invalidate_range_start(mm, start, end, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm,
|
||||
|
@ -461,6 +472,12 @@ static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm,
|
|||
{
|
||||
}
|
||||
|
||||
static inline int mmu_notifier_invalidate_range_start_nonblock(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
|
|
|
@ -755,25 +755,6 @@ static inline bool pgdat_is_empty(pg_data_t *pgdat)
|
|||
return !pgdat->node_start_pfn && !pgdat->node_spanned_pages;
|
||||
}
|
||||
|
||||
static inline int zone_id(const struct zone *zone)
|
||||
{
|
||||
struct pglist_data *pgdat = zone->zone_pgdat;
|
||||
|
||||
return zone - pgdat->node_zones;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ZONE_DEVICE
|
||||
static inline bool is_dev_zone(const struct zone *zone)
|
||||
{
|
||||
return zone_id(zone) == ZONE_DEVICE;
|
||||
}
|
||||
#else
|
||||
static inline bool is_dev_zone(const struct zone *zone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <linux/memory_hotplug.h>
|
||||
|
||||
void build_all_zonelists(pg_data_t *pgdat);
|
||||
|
@ -824,6 +805,18 @@ static inline int local_memory_node(int node_id) { return node_id; };
|
|||
*/
|
||||
#define zone_idx(zone) ((zone) - (zone)->zone_pgdat->node_zones)
|
||||
|
||||
#ifdef CONFIG_ZONE_DEVICE
|
||||
static inline bool is_dev_zone(const struct zone *zone)
|
||||
{
|
||||
return zone_idx(zone) == ZONE_DEVICE;
|
||||
}
|
||||
#else
|
||||
static inline bool is_dev_zone(const struct zone *zone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns true if a zone has pages managed by the buddy allocator.
|
||||
* All the reclaim decisions have to use this function rather than
|
||||
|
@ -841,6 +834,25 @@ static inline bool populated_zone(struct zone *zone)
|
|||
return zone->present_pages;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
static inline int zone_to_nid(struct zone *zone)
|
||||
{
|
||||
return zone->node;
|
||||
}
|
||||
|
||||
static inline void zone_set_nid(struct zone *zone, int nid)
|
||||
{
|
||||
zone->node = nid;
|
||||
}
|
||||
#else
|
||||
static inline int zone_to_nid(struct zone *zone)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void zone_set_nid(struct zone *zone, int nid) {}
|
||||
#endif
|
||||
|
||||
extern int movable_zone;
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
|
@ -956,12 +968,7 @@ static inline int zonelist_zone_idx(struct zoneref *zoneref)
|
|||
|
||||
static inline int zonelist_node_idx(struct zoneref *zoneref)
|
||||
{
|
||||
#ifdef CONFIG_NUMA
|
||||
/* zone_to_nid not available in this context */
|
||||
return zoneref->zone->node;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_NUMA */
|
||||
return zone_to_nid(zoneref->zone);
|
||||
}
|
||||
|
||||
struct zoneref *__next_zones_zonelist(struct zoneref *z,
|
||||
|
|
|
@ -363,7 +363,6 @@ static inline void net_dim_sample(u16 event_ctr,
|
|||
}
|
||||
|
||||
#define NET_DIM_NEVENTS 64
|
||||
#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE)
|
||||
#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1))
|
||||
|
||||
static inline void net_dim_calc_stats(struct net_dim_sample *start,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue