mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
Merge remote-tracking branch 'sm8350/lineage-20' into lineage-23.0
* sm8350/lineage-20: (841 commits)
UPSTREAM: fs: prevent out-of-bounds array speculation when closing a file descriptor
UPSTREAM: file: let pick_file() tell caller it's done
BACKPORT: rcu-tasks: Add a grace-period start time for throttling and debug
BACKPORT: maccess: rename probe_user_{read,write} to copy_{from,to}_user_nofault
BACKPORT: powerpc: use probe_user_read() and probe_user_write()
BACKPORT: maccess: rename probe_kernel_address to get_kernel_nofault
BACKPORT: tracing/kprobes: handle mixed kernel/userspace probes better
BACKPORT: maccess: rename probe_kernel_{read,write} to copy_{from,to}_kernel_nofault
UPSTREAM: maccess: unexport probe_kernel_write()
UPSTREAM: maccess: return -ERANGE when probe_kernel_read() fails
UPSTREAM: maccess: allow architectures to provide kernel probing directly
BACKPORT: maccess: remove strncpy_from_unsafe
UPSTREAM: bpf:bpf_seq_printf(): handle potentially unsafe format string better
UPSTREAM: bpf: Use strncpy_from_unsafe_strict() in bpf_seq_printf() helper
UPSTREAM: bpf: handle the compat string in bpf_trace_copy_string better
UPSTREAM: bpf: factor out a bpf_trace_copy_string helper
BACKPORT: maccess: always use strict semantics for probe_kernel_read
UPSTREAM: maccess: unify the probe kernel arch hooks
UPSTREAM: maccess: remove probe_read_common and probe_write_common
UPSTREAM: maccess: remove duplicate kerneldoc comments
...
Change-Id: I22cf7d6fb34826727904effb4f012b0ef459149d
This commit is contained in:
commit
2eb0f824a1
529 changed files with 40893 additions and 9494 deletions
|
|
@ -2453,7 +2453,7 @@ int sysctl_compact_memory;
|
|||
* /proc/sys/vm/compact_memory
|
||||
*/
|
||||
int sysctl_compaction_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
if (write)
|
||||
compact_nodes();
|
||||
|
|
|
|||
|
|
@ -3145,7 +3145,7 @@ static int proc_hugetlb_doulongvec_minmax(struct ctl_table *table, int write,
|
|||
|
||||
static int hugetlb_sysctl_handler_common(bool obey_mempolicy,
|
||||
struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
struct hstate *h = &default_hstate;
|
||||
unsigned long tmp = h->max_huge_pages;
|
||||
|
|
@ -3167,7 +3167,7 @@ out:
|
|||
}
|
||||
|
||||
int hugetlb_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
|
||||
return hugetlb_sysctl_handler_common(false, table, write,
|
||||
|
|
@ -3176,7 +3176,7 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
|
|||
|
||||
#ifdef CONFIG_NUMA
|
||||
int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
return hugetlb_sysctl_handler_common(true, table, write,
|
||||
buffer, length, ppos);
|
||||
|
|
@ -3184,8 +3184,7 @@ int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
|
|||
#endif /* CONFIG_NUMA */
|
||||
|
||||
int hugetlb_overcommit_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer,
|
||||
size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
struct hstate *h = &default_hstate;
|
||||
unsigned long tmp;
|
||||
|
|
|
|||
188
mm/maccess.c
188
mm/maccess.c
|
|
@ -6,63 +6,125 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static __always_inline long
|
||||
probe_read_common(void *dst, const void __user *src, size_t size)
|
||||
bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
|
||||
size_t size)
|
||||
{
|
||||
long ret;
|
||||
|
||||
pagefault_disable();
|
||||
ret = __copy_from_user_inatomic(dst, src, size);
|
||||
pagefault_enable();
|
||||
|
||||
return ret ? -EFAULT : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static __always_inline long
|
||||
probe_write_common(void __user *dst, const void *src, size_t size)
|
||||
#ifdef HAVE_GET_KERNEL_NOFAULT
|
||||
|
||||
#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \
|
||||
while (len >= sizeof(type)) { \
|
||||
__get_kernel_nofault(dst, src, type, err_label); \
|
||||
dst += sizeof(type); \
|
||||
src += sizeof(type); \
|
||||
len -= sizeof(type); \
|
||||
}
|
||||
|
||||
long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret;
|
||||
if (!copy_from_kernel_nofault_allowed(src, size))
|
||||
return -ERANGE;
|
||||
|
||||
pagefault_disable();
|
||||
ret = __copy_to_user_inatomic(dst, src, size);
|
||||
copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
|
||||
copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
|
||||
copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
|
||||
copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
|
||||
pagefault_enable();
|
||||
return 0;
|
||||
Efault:
|
||||
pagefault_enable();
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
|
||||
|
||||
return ret ? -EFAULT : 0;
|
||||
#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \
|
||||
while (len >= sizeof(type)) { \
|
||||
__put_kernel_nofault(dst, src, type, err_label); \
|
||||
dst += sizeof(type); \
|
||||
src += sizeof(type); \
|
||||
len -= sizeof(type); \
|
||||
}
|
||||
|
||||
long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
|
||||
{
|
||||
pagefault_disable();
|
||||
copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
|
||||
copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
|
||||
copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
|
||||
copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
|
||||
pagefault_enable();
|
||||
return 0;
|
||||
Efault:
|
||||
pagefault_enable();
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
|
||||
{
|
||||
const void *src = unsafe_addr;
|
||||
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
|
||||
return -ERANGE;
|
||||
|
||||
pagefault_disable();
|
||||
do {
|
||||
__get_kernel_nofault(dst, src, u8, Efault);
|
||||
dst++;
|
||||
src++;
|
||||
} while (dst[-1] && src - unsafe_addr < count);
|
||||
pagefault_enable();
|
||||
|
||||
dst[-1] = '\0';
|
||||
return src - unsafe_addr;
|
||||
Efault:
|
||||
pagefault_enable();
|
||||
dst[-1] = '\0';
|
||||
return -EFAULT;
|
||||
}
|
||||
#else /* HAVE_GET_KERNEL_NOFAULT */
|
||||
/**
|
||||
* probe_kernel_read(): safely attempt to read from a kernel-space location
|
||||
* copy_from_kernel_nofault(): safely attempt to read from kernel-space
|
||||
* @dst: pointer to the buffer that shall take the data
|
||||
* @src: address to read from
|
||||
* @size: size of the data chunk
|
||||
*
|
||||
* Safely read from address @src to the buffer at @dst. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
* Safely read from kernel address @src to the buffer at @dst. If a kernel
|
||||
* fault happens, handle that and return -EFAULT. If @src is not a valid kernel
|
||||
* address, return -ERANGE.
|
||||
*
|
||||
* We ensure that the copy_from_user is executed in atomic context so that
|
||||
* do_page_fault() doesn't attempt to take mmap_sem. This makes
|
||||
* probe_kernel_read() suitable for use within regions where the caller
|
||||
* copy_from_kernel_nofault() suitable for use within regions where the caller
|
||||
* already holds mmap_sem, or other locks which nest inside mmap_sem.
|
||||
*/
|
||||
|
||||
long __weak probe_kernel_read(void *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_kernel_read")));
|
||||
|
||||
long __probe_kernel_read(void *dst, const void *src, size_t size)
|
||||
long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
if (!copy_from_kernel_nofault_allowed(src, size))
|
||||
return -ERANGE;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = probe_read_common(dst, (__force const void __user *)src, size);
|
||||
pagefault_disable();
|
||||
ret = __copy_from_user_inatomic(dst, (__force const void __user *)src,
|
||||
size);
|
||||
pagefault_enable();
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_kernel_read);
|
||||
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
|
||||
|
||||
/**
|
||||
* probe_user_read(): safely attempt to read from a user-space location
|
||||
* copy_from_user_nofault(): safely attempt to read from a user-space location
|
||||
* @dst: pointer to the buffer that shall take the data
|
||||
* @src: address to read from. This must be a user address.
|
||||
* @size: size of the data chunk
|
||||
|
|
@ -70,23 +132,24 @@ EXPORT_SYMBOL_GPL(probe_kernel_read);
|
|||
* Safely read from user address @src to the buffer at @dst. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
|
||||
long __weak probe_user_read(void *dst, const void __user *src, size_t size)
|
||||
__attribute__((alias("__probe_user_read")));
|
||||
|
||||
long __probe_user_read(void *dst, const void __user *src, size_t size)
|
||||
long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
|
||||
{
|
||||
long ret = -EFAULT;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(USER_DS);
|
||||
if (access_ok(src, size))
|
||||
ret = probe_read_common(dst, src, size);
|
||||
if (access_ok(src, size)) {
|
||||
pagefault_disable();
|
||||
ret = __copy_from_user_inatomic(dst, src, size);
|
||||
pagefault_enable();
|
||||
}
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_user_read);
|
||||
EXPORT_SYMBOL_GPL(copy_from_user_nofault);
|
||||
|
||||
/**
|
||||
* probe_kernel_write(): safely attempt to write to a location
|
||||
|
|
@ -97,25 +160,24 @@ EXPORT_SYMBOL_GPL(probe_user_read);
|
|||
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
|
||||
long __weak probe_kernel_write(void *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_kernel_write")));
|
||||
|
||||
long __probe_kernel_write(void *dst, const void *src, size_t size)
|
||||
long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = probe_write_common((__force void __user *)dst, src, size);
|
||||
pagefault_disable();
|
||||
ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
|
||||
pagefault_enable();
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_kernel_write);
|
||||
|
||||
/**
|
||||
* probe_user_write(): safely attempt to write to a user-space location
|
||||
* copy_to_user_nofault(): safely attempt to write to a user-space location
|
||||
* @dst: address to write to
|
||||
* @src: pointer to the data that shall be written
|
||||
* @size: size of the data chunk
|
||||
|
|
@ -123,26 +185,28 @@ EXPORT_SYMBOL_GPL(probe_kernel_write);
|
|||
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
|
||||
long __weak probe_user_write(void __user *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_user_write")));
|
||||
|
||||
long __probe_user_write(void __user *dst, const void *src, size_t size)
|
||||
long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret = -EFAULT;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(USER_DS);
|
||||
if (access_ok(dst, size))
|
||||
ret = probe_write_common(dst, src, size);
|
||||
if (access_ok(dst, size)) {
|
||||
pagefault_disable();
|
||||
ret = __copy_to_user_inatomic(dst, src, size);
|
||||
pagefault_enable();
|
||||
}
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_user_write);
|
||||
EXPORT_SYMBOL_GPL(copy_to_user_nofault);
|
||||
|
||||
/**
|
||||
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
|
||||
* strncpy_from_kernel_nofault: - Copy a NUL terminated string from unsafe
|
||||
* address.
|
||||
* @dst: Destination address, in kernel space. This buffer must be at
|
||||
* least @count bytes long.
|
||||
* @unsafe_addr: Unsafe address.
|
||||
|
|
@ -152,13 +216,14 @@ EXPORT_SYMBOL_GPL(probe_user_write);
|
|||
*
|
||||
* On success, returns the length of the string INCLUDING the trailing NUL.
|
||||
*
|
||||
* If access fails, returns -EFAULT (some data may have been copied
|
||||
* and the trailing NUL added).
|
||||
* If access fails, returns -EFAULT (some data may have been copied and the
|
||||
* trailing NUL added). If @unsafe_addr is not a valid kernel address, return
|
||||
* -ERANGE.
|
||||
*
|
||||
* If @count is smaller than the length of the string, copies @count-1 bytes,
|
||||
* sets the last byte of @dst buffer to NUL and returns @count.
|
||||
*/
|
||||
long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
||||
long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
const void *src = unsafe_addr;
|
||||
|
|
@ -166,6 +231,8 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
|||
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
|
||||
return -ERANGE;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
pagefault_disable();
|
||||
|
|
@ -180,9 +247,10 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
|||
|
||||
return ret ? -EFAULT : src - unsafe_addr;
|
||||
}
|
||||
#endif /* HAVE_GET_KERNEL_NOFAULT */
|
||||
|
||||
/**
|
||||
* strncpy_from_unsafe_user: - Copy a NUL terminated string from unsafe user
|
||||
* strncpy_from_user_nofault: - Copy a NUL terminated string from unsafe user
|
||||
* address.
|
||||
* @dst: Destination address, in kernel space. This buffer must be at
|
||||
* least @count bytes long.
|
||||
|
|
@ -199,7 +267,7 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
|||
* If @count is smaller than the length of the string, copies @count-1 bytes,
|
||||
* sets the last byte of @dst buffer to NUL and returns @count.
|
||||
*/
|
||||
long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
|
||||
long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
|
|
|||
220
mm/memcontrol.c
220
mm/memcontrol.c
|
|
@ -6405,6 +6405,117 @@ struct cgroup_subsys memory_cgrp_subsys = {
|
|||
.early_init = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function calculates an individual cgroup's effective
|
||||
* protection which is derived from its own memory.min/low, its
|
||||
* parent's and siblings' settings, as well as the actual memory
|
||||
* distribution in the tree.
|
||||
*
|
||||
* The following rules apply to the effective protection values:
|
||||
*
|
||||
* 1. At the first level of reclaim, effective protection is equal to
|
||||
* the declared protection in memory.min and memory.low.
|
||||
*
|
||||
* 2. To enable safe delegation of the protection configuration, at
|
||||
* subsequent levels the effective protection is capped to the
|
||||
* parent's effective protection.
|
||||
*
|
||||
* 3. To make complex and dynamic subtrees easier to configure, the
|
||||
* user is allowed to overcommit the declared protection at a given
|
||||
* level. If that is the case, the parent's effective protection is
|
||||
* distributed to the children in proportion to how much protection
|
||||
* they have declared and how much of it they are utilizing.
|
||||
*
|
||||
* This makes distribution proportional, but also work-conserving:
|
||||
* if one cgroup claims much more protection than it uses memory,
|
||||
* the unused remainder is available to its siblings.
|
||||
*
|
||||
* 4. Conversely, when the declared protection is undercommitted at a
|
||||
* given level, the distribution of the larger parental protection
|
||||
* budget is NOT proportional. A cgroup's protection from a sibling
|
||||
* is capped to its own memory.min/low setting.
|
||||
*
|
||||
* 5. However, to allow protecting recursive subtrees from each other
|
||||
* without having to declare each individual cgroup's fixed share
|
||||
* of the ancestor's claim to protection, any unutilized -
|
||||
* "floating" - protection from up the tree is distributed in
|
||||
* proportion to each cgroup's *usage*. This makes the protection
|
||||
* neutral wrt sibling cgroups and lets them compete freely over
|
||||
* the shared parental protection budget, but it protects the
|
||||
* subtree as a whole from neighboring subtrees.
|
||||
*
|
||||
* Note that 4. and 5. are not in conflict: 4. is about protecting
|
||||
* against immediate siblings whereas 5. is about protecting against
|
||||
* neighboring subtrees.
|
||||
*/
|
||||
static unsigned long effective_protection(unsigned long usage,
|
||||
unsigned long parent_usage,
|
||||
unsigned long setting,
|
||||
unsigned long parent_effective,
|
||||
unsigned long siblings_protected)
|
||||
{
|
||||
unsigned long protected;
|
||||
unsigned long ep;
|
||||
|
||||
protected = min(usage, setting);
|
||||
/*
|
||||
* If all cgroups at this level combined claim and use more
|
||||
* protection then what the parent affords them, distribute
|
||||
* shares in proportion to utilization.
|
||||
*
|
||||
* We are using actual utilization rather than the statically
|
||||
* claimed protection in order to be work-conserving: claimed
|
||||
* but unused protection is available to siblings that would
|
||||
* otherwise get a smaller chunk than what they claimed.
|
||||
*/
|
||||
if (siblings_protected > parent_effective)
|
||||
return protected * parent_effective / siblings_protected;
|
||||
|
||||
/*
|
||||
* Ok, utilized protection of all children is within what the
|
||||
* parent affords them, so we know whatever this child claims
|
||||
* and utilizes is effectively protected.
|
||||
*
|
||||
* If there is unprotected usage beyond this value, reclaim
|
||||
* will apply pressure in proportion to that amount.
|
||||
*
|
||||
* If there is unutilized protection, the cgroup will be fully
|
||||
* shielded from reclaim, but we do return a smaller value for
|
||||
* protection than what the group could enjoy in theory. This
|
||||
* is okay. With the overcommit distribution above, effective
|
||||
* protection is always dependent on how memory is actually
|
||||
* consumed among the siblings anyway.
|
||||
*/
|
||||
ep = protected;
|
||||
|
||||
/*
|
||||
* If the children aren't claiming (all of) the protection
|
||||
* afforded to them by the parent, distribute the remainder in
|
||||
* proportion to the (unprotected) memory of each cgroup. That
|
||||
* way, cgroups that aren't explicitly prioritized wrt each
|
||||
* other compete freely over the allowance, but they are
|
||||
* collectively protected from neighboring trees.
|
||||
*
|
||||
* We're using unprotected memory for the weight so that if
|
||||
* some cgroups DO claim explicit protection, we don't protect
|
||||
* the same bytes twice.
|
||||
*/
|
||||
if (!(cgrp_dfl_root.flags & CGRP_ROOT_MEMORY_RECURSIVE_PROT))
|
||||
return ep;
|
||||
|
||||
if (parent_effective > siblings_protected && usage > protected) {
|
||||
unsigned long unclaimed;
|
||||
|
||||
unclaimed = parent_effective - siblings_protected;
|
||||
unclaimed *= usage - protected;
|
||||
unclaimed /= parent_usage - siblings_protected;
|
||||
|
||||
ep += unclaimed;
|
||||
}
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
/**
|
||||
* mem_cgroup_protected - check if memory consumption is in the normal range
|
||||
* @root: the top ancestor of the sub-tree being checked
|
||||
|
|
@ -6418,70 +6529,12 @@ struct cgroup_subsys memory_cgrp_subsys = {
|
|||
* MEMCG_PROT_LOW: cgroup memory is protected as long there is
|
||||
* an unprotected supply of reclaimable memory from other cgroups.
|
||||
* MEMCG_PROT_MIN: cgroup memory is protected
|
||||
*
|
||||
* @root is exclusive; it is never protected when looked at directly
|
||||
*
|
||||
* To provide a proper hierarchical behavior, effective memory.min/low values
|
||||
* are used. Below is the description of how effective memory.low is calculated.
|
||||
* Effective memory.min values is calculated in the same way.
|
||||
*
|
||||
* Effective memory.low is always equal or less than the original memory.low.
|
||||
* If there is no memory.low overcommittment (which is always true for
|
||||
* top-level memory cgroups), these two values are equal.
|
||||
* Otherwise, it's a part of parent's effective memory.low,
|
||||
* calculated as a cgroup's memory.low usage divided by sum of sibling's
|
||||
* memory.low usages, where memory.low usage is the size of actually
|
||||
* protected memory.
|
||||
*
|
||||
* low_usage
|
||||
* elow = min( memory.low, parent->elow * ------------------ ),
|
||||
* siblings_low_usage
|
||||
*
|
||||
* | memory.current, if memory.current < memory.low
|
||||
* low_usage = |
|
||||
* | 0, otherwise.
|
||||
*
|
||||
*
|
||||
* Such definition of the effective memory.low provides the expected
|
||||
* hierarchical behavior: parent's memory.low value is limiting
|
||||
* children, unprotected memory is reclaimed first and cgroups,
|
||||
* which are not using their guarantee do not affect actual memory
|
||||
* distribution.
|
||||
*
|
||||
* For example, if there are memcgs A, A/B, A/C, A/D and A/E:
|
||||
*
|
||||
* A A/memory.low = 2G, A/memory.current = 6G
|
||||
* //\\
|
||||
* BC DE B/memory.low = 3G B/memory.current = 2G
|
||||
* C/memory.low = 1G C/memory.current = 2G
|
||||
* D/memory.low = 0 D/memory.current = 2G
|
||||
* E/memory.low = 10G E/memory.current = 0
|
||||
*
|
||||
* and the memory pressure is applied, the following memory distribution
|
||||
* is expected (approximately):
|
||||
*
|
||||
* A/memory.current = 2G
|
||||
*
|
||||
* B/memory.current = 1.3G
|
||||
* C/memory.current = 0.6G
|
||||
* D/memory.current = 0
|
||||
* E/memory.current = 0
|
||||
*
|
||||
* These calculations require constant tracking of the actual low usages
|
||||
* (see propagate_protected_usage()), as well as recursive calculation of
|
||||
* effective memory.low values. But as we do call mem_cgroup_protected()
|
||||
* path for each memory cgroup top-down from the reclaim,
|
||||
* it's possible to optimize this part, and save calculated elow
|
||||
* for next usage. This part is intentionally racy, but it's ok,
|
||||
* as memory.low is a best-effort mechanism.
|
||||
*/
|
||||
enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root,
|
||||
struct mem_cgroup *memcg)
|
||||
{
|
||||
unsigned long usage, parent_usage;
|
||||
struct mem_cgroup *parent;
|
||||
unsigned long emin, parent_emin;
|
||||
unsigned long elow, parent_elow;
|
||||
unsigned long usage;
|
||||
|
||||
if (mem_cgroup_disabled())
|
||||
return MEMCG_PROT_NONE;
|
||||
|
|
@ -6503,52 +6556,31 @@ enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root,
|
|||
if (!usage)
|
||||
return MEMCG_PROT_NONE;
|
||||
|
||||
emin = memcg->memory.min;
|
||||
elow = memcg->memory.low;
|
||||
|
||||
parent = parent_mem_cgroup(memcg);
|
||||
/* No parent means a non-hierarchical mode on v1 memcg */
|
||||
if (!parent)
|
||||
return MEMCG_PROT_NONE;
|
||||
|
||||
if (parent == root)
|
||||
goto exit;
|
||||
|
||||
parent_emin = READ_ONCE(parent->memory.emin);
|
||||
emin = min(emin, parent_emin);
|
||||
if (emin && parent_emin) {
|
||||
unsigned long min_usage, siblings_min_usage;
|
||||
|
||||
min_usage = min(usage, memcg->memory.min);
|
||||
siblings_min_usage = atomic_long_read(
|
||||
&parent->memory.children_min_usage);
|
||||
|
||||
if (min_usage && siblings_min_usage)
|
||||
emin = min(emin, parent_emin * min_usage /
|
||||
siblings_min_usage);
|
||||
if (parent == root) {
|
||||
memcg->memory.emin = memcg->memory.min;
|
||||
memcg->memory.elow = memcg->memory.low;
|
||||
goto out;
|
||||
}
|
||||
|
||||
parent_elow = READ_ONCE(parent->memory.elow);
|
||||
elow = min(elow, parent_elow);
|
||||
if (elow && parent_elow) {
|
||||
unsigned long low_usage, siblings_low_usage;
|
||||
parent_usage = page_counter_read(&parent->memory);
|
||||
|
||||
low_usage = min(usage, memcg->memory.low);
|
||||
siblings_low_usage = atomic_long_read(
|
||||
&parent->memory.children_low_usage);
|
||||
memcg->memory.emin = effective_protection(usage, parent_usage,
|
||||
memcg->memory.min, READ_ONCE(parent->memory.emin),
|
||||
atomic_long_read(&parent->memory.children_min_usage));
|
||||
|
||||
if (low_usage && siblings_low_usage)
|
||||
elow = min(elow, parent_elow * low_usage /
|
||||
siblings_low_usage);
|
||||
}
|
||||
memcg->memory.elow = effective_protection(usage, parent_usage,
|
||||
memcg->memory.low, READ_ONCE(parent->memory.elow),
|
||||
atomic_long_read(&parent->memory.children_low_usage));
|
||||
|
||||
exit:
|
||||
memcg->memory.emin = emin;
|
||||
memcg->memory.elow = elow;
|
||||
|
||||
if (usage <= emin)
|
||||
out:
|
||||
if (usage <= memcg->memory.emin)
|
||||
return MEMCG_PROT_MIN;
|
||||
else if (usage <= elow)
|
||||
else if (usage <= memcg->memory.elow)
|
||||
return MEMCG_PROT_LOW;
|
||||
else
|
||||
return MEMCG_PROT_NONE;
|
||||
|
|
|
|||
|
|
@ -2539,6 +2539,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
|
|||
}
|
||||
flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte));
|
||||
entry = mk_pte(new_page, vma->vm_page_prot);
|
||||
entry = pte_sw_mkyoung(entry);
|
||||
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
|
||||
/*
|
||||
* Clear the pte entry and flush it first, before updating the
|
||||
|
|
@ -3223,6 +3224,7 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
|
|||
__SetPageUptodate(page);
|
||||
|
||||
entry = mk_pte(page, vma->vm_page_prot);
|
||||
entry = pte_sw_mkyoung(entry);
|
||||
if (vma->vm_flags & VM_WRITE)
|
||||
entry = pte_mkwrite(pte_mkdirty(entry));
|
||||
|
||||
|
|
@ -3511,6 +3513,7 @@ vm_fault_t alloc_set_pte(struct vm_fault *vmf, struct mem_cgroup *memcg,
|
|||
|
||||
flush_icache_page(vma, page);
|
||||
entry = mk_pte(page, vma->vm_page_prot);
|
||||
entry = pte_sw_mkyoung(entry);
|
||||
if (write)
|
||||
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
|
||||
|
||||
|
|
|
|||
15
mm/nommu.c
15
mm/nommu.c
|
|
@ -140,7 +140,7 @@ void vfree(const void *addr)
|
|||
}
|
||||
EXPORT_SYMBOL(vfree);
|
||||
|
||||
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
|
||||
void *__vmalloc(unsigned long size, gfp_t gfp_mask)
|
||||
{
|
||||
/*
|
||||
* You can't specify __GFP_HIGHMEM with kmalloc() since kmalloc()
|
||||
|
|
@ -152,14 +152,14 @@ EXPORT_SYMBOL(__vmalloc);
|
|||
|
||||
void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags)
|
||||
{
|
||||
return __vmalloc(size, flags, PAGE_KERNEL);
|
||||
return __vmalloc(size, flags);
|
||||
}
|
||||
|
||||
void *vmalloc_user(unsigned long size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = __vmalloc(size, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL);
|
||||
ret = __vmalloc(size, GFP_KERNEL | __GFP_ZERO);
|
||||
if (ret) {
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ long vwrite(char *buf, char *addr, unsigned long count)
|
|||
*/
|
||||
void *vmalloc(unsigned long size)
|
||||
{
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM);
|
||||
}
|
||||
EXPORT_SYMBOL(vmalloc);
|
||||
|
||||
|
|
@ -237,8 +237,7 @@ EXPORT_SYMBOL(vmalloc);
|
|||
*/
|
||||
void *vzalloc(unsigned long size)
|
||||
{
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
|
||||
PAGE_KERNEL);
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
|
||||
}
|
||||
EXPORT_SYMBOL(vzalloc);
|
||||
|
||||
|
|
@ -291,7 +290,7 @@ EXPORT_SYMBOL(vzalloc_node);
|
|||
|
||||
void *vmalloc_exec(unsigned long size)
|
||||
{
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC);
|
||||
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -303,7 +302,7 @@ void *vmalloc_exec(unsigned long size)
|
|||
*/
|
||||
void *vmalloc_32(unsigned long size)
|
||||
{
|
||||
return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL);
|
||||
return __vmalloc(size, GFP_KERNEL);
|
||||
}
|
||||
EXPORT_SYMBOL(vmalloc_32);
|
||||
|
||||
|
|
|
|||
|
|
@ -523,8 +523,7 @@ bool node_dirty_ok(struct pglist_data *pgdat)
|
|||
}
|
||||
|
||||
int dirty_background_ratio_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -535,8 +534,7 @@ int dirty_background_ratio_handler(struct ctl_table *table, int write,
|
|||
}
|
||||
|
||||
int dirty_background_bytes_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned long old_bytes = dirty_background_bytes;
|
||||
|
|
@ -553,9 +551,8 @@ int dirty_background_bytes_handler(struct ctl_table *table, int write,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int dirty_ratio_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
int dirty_ratio_handler(struct ctl_table *table, int write, void *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int old_ratio = vm_dirty_ratio;
|
||||
int ret;
|
||||
|
|
@ -569,8 +566,7 @@ int dirty_ratio_handler(struct ctl_table *table, int write,
|
|||
}
|
||||
|
||||
int dirty_bytes_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
unsigned long old_bytes = vm_dirty_bytes;
|
||||
int ret;
|
||||
|
|
@ -1994,7 +1990,7 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb)
|
|||
* sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
|
||||
*/
|
||||
int dirty_writeback_centisecs_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
unsigned int old_interval = dirty_writeback_interval;
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -5730,21 +5730,11 @@ char numa_zonelist_order[] = "Node";
|
|||
* sysctl handler for numa_zonelist_order
|
||||
*/
|
||||
int numa_zonelist_order_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length,
|
||||
loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
char *str;
|
||||
int ret;
|
||||
|
||||
if (!write)
|
||||
return proc_dostring(table, write, buffer, length, ppos);
|
||||
str = memdup_user_nul(buffer, 16);
|
||||
if (IS_ERR(str))
|
||||
return PTR_ERR(str);
|
||||
|
||||
ret = __parse_numa_zonelist_order(str);
|
||||
kfree(str);
|
||||
return ret;
|
||||
if (write)
|
||||
return __parse_numa_zonelist_order(buffer);
|
||||
return proc_dostring(table, write, buffer, length, ppos);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -8155,7 +8145,7 @@ postcore_initcall(init_per_zone_wmark_min)
|
|||
* or extra_free_kbytes changes.
|
||||
*/
|
||||
int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -8199,7 +8189,7 @@ int kswapd_threads_sysctl_handler(struct ctl_table *table, int write,
|
|||
}
|
||||
#endif
|
||||
int watermark_scale_factor_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -8229,7 +8219,7 @@ static void setup_min_unmapped_ratio(void)
|
|||
|
||||
|
||||
int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -8256,7 +8246,7 @@ static void setup_min_slab_ratio(void)
|
|||
}
|
||||
|
||||
int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -8280,7 +8270,7 @@ int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *table, int write,
|
|||
* if in function of the boot time zone sizes.
|
||||
*/
|
||||
int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
proc_dointvec_minmax(table, write, buffer, length, ppos);
|
||||
setup_per_zone_lowmem_reserve();
|
||||
|
|
@ -8293,7 +8283,7 @@ int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *table, int write,
|
|||
* pagelist can have before it gets flushed back to buddy allocator.
|
||||
*/
|
||||
int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
struct zone *zone;
|
||||
int old_percpu_pagelist_fraction;
|
||||
|
|
@ -8442,7 +8432,7 @@ void *__init alloc_large_system_hash(const char *tablename,
|
|||
table = memblock_alloc_raw(size,
|
||||
SMP_CACHE_BYTES);
|
||||
} else if (get_order(size) >= MAX_ORDER || hashdist) {
|
||||
table = __vmalloc(size, gfp_flags, PAGE_KERNEL);
|
||||
table = __vmalloc(size, gfp_flags);
|
||||
virt = true;
|
||||
} else {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@ static void *pcpu_mem_zalloc(size_t size, gfp_t gfp)
|
|||
if (size <= PAGE_SIZE)
|
||||
return kzalloc(size, gfp);
|
||||
else
|
||||
return __vmalloc(size, gfp | __GFP_ZERO, PAGE_KERNEL);
|
||||
return __vmalloc(size, gfp | __GFP_ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ void rodata_test(void)
|
|||
}
|
||||
|
||||
/* test 2: write to the variable; this should fault */
|
||||
if (!probe_kernel_write((void *)&rodata_test_data,
|
||||
if (!copy_to_kernel_nofault((void *)&rodata_test_data,
|
||||
(void *)&zero, sizeof(zero))) {
|
||||
pr_err("test data was not read only\n");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -3203,7 +3203,7 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler,
|
|||
struct shmem_inode_info *info = SHMEM_I(inode);
|
||||
|
||||
name = xattr_full_name(handler, name);
|
||||
return simple_xattr_set(&info->xattrs, name, value, size, flags);
|
||||
return simple_xattr_set(&info->xattrs, name, value, size, flags, NULL);
|
||||
}
|
||||
|
||||
static const struct xattr_handler shmem_security_xattr_handler = {
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
|
|||
return get_freepointer(s, object);
|
||||
|
||||
freepointer_addr = (unsigned long)object + s->offset;
|
||||
probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
|
||||
copy_from_kernel_nofault(&p, (void **)freepointer_addr, sizeof(p));
|
||||
return freelist_ptr(s, p, freepointer_addr);
|
||||
}
|
||||
|
||||
|
|
|
|||
10
mm/util.c
10
mm/util.c
|
|
@ -757,9 +757,8 @@ int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
|
|||
unsigned long sysctl_user_reserve_kbytes __read_mostly = 1UL << 17; /* 128MB */
|
||||
unsigned long sysctl_admin_reserve_kbytes __read_mostly = 1UL << 13; /* 8MB */
|
||||
|
||||
int overcommit_ratio_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
int overcommit_ratio_handler(struct ctl_table *table, int write, void *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -769,9 +768,8 @@ int overcommit_ratio_handler(struct ctl_table *table, int write,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int overcommit_kbytes_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
int overcommit_kbytes_handler(struct ctl_table *table, int write, void *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
24
mm/vmalloc.c
24
mm/vmalloc.c
|
|
@ -2643,9 +2643,9 @@ static void *__vmalloc_node(unsigned long size, unsigned long align,
|
|||
#endif
|
||||
}
|
||||
|
||||
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
|
||||
void *__vmalloc(unsigned long size, gfp_t gfp_mask)
|
||||
{
|
||||
return __vmalloc_node(size, 1, gfp_mask, prot, NUMA_NO_NODE,
|
||||
return __vmalloc_node(size, 1, gfp_mask, PAGE_KERNEL, NUMA_NO_NODE,
|
||||
__builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(__vmalloc);
|
||||
|
|
@ -2762,6 +2762,26 @@ void *vzalloc_node(unsigned long size, int node)
|
|||
}
|
||||
EXPORT_SYMBOL(vzalloc_node);
|
||||
|
||||
/**
|
||||
* vmalloc_user_node_flags - allocate memory for userspace on a specific node
|
||||
* @size: allocation size
|
||||
* @node: numa node
|
||||
* @flags: flags for the page level allocator
|
||||
*
|
||||
* The resulting memory area is zeroed so it can be mapped to userspace
|
||||
* without leaking data.
|
||||
*
|
||||
* Return: pointer to the allocated memory or %NULL on error
|
||||
*/
|
||||
void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags)
|
||||
{
|
||||
return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END,
|
||||
flags | __GFP_ZERO, PAGE_KERNEL,
|
||||
VM_USERMAP, node,
|
||||
__builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(vmalloc_user_node_flags);
|
||||
|
||||
/**
|
||||
* vmalloc_exec - allocate virtually contiguous, executable memory
|
||||
* @size: allocation size
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ static void invalid_numa_statistics(void)
|
|||
static DEFINE_MUTEX(vm_numa_stat_lock);
|
||||
|
||||
int sysctl_vm_numa_stat_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *length, loff_t *ppos)
|
||||
void *buffer, size_t *length, loff_t *ppos)
|
||||
{
|
||||
int ret, oldval;
|
||||
|
||||
|
|
@ -1761,7 +1761,7 @@ static void refresh_vm_stats(struct work_struct *work)
|
|||
}
|
||||
|
||||
int vmstat_refresh(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
long val;
|
||||
int err;
|
||||
|
|
|
|||
Loading…
Reference in a new issue