|
@@ -1111,6 +1111,23 @@ static bool mem_cgroup_check_under_limit(struct mem_cgroup *mem)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * mem_cgroup_check_margin - check if the memory cgroup allows charging
|
|
|
+ * @mem: memory cgroup to check
|
|
|
+ * @bytes: the number of bytes the caller intends to charge
|
|
|
+ *
|
|
|
+ * Returns a boolean value on whether @mem can be charged @bytes or
|
|
|
+ * whether this would exceed the limit.
|
|
|
+ */
|
|
|
+static bool mem_cgroup_check_margin(struct mem_cgroup *mem, unsigned long bytes)
|
|
|
+{
|
|
|
+ if (!res_counter_check_margin(&mem->res, bytes))
|
|
|
+ return false;
|
|
|
+ if (do_swap_account && !res_counter_check_margin(&mem->memsw, bytes))
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static unsigned int get_swappiness(struct mem_cgroup *memcg)
|
|
|
{
|
|
|
struct cgroup *cgrp = memcg->css.cgroup;
|
|
@@ -1852,15 +1869,19 @@ static int __mem_cgroup_do_charge(struct mem_cgroup *mem, gfp_t gfp_mask,
|
|
|
return CHARGE_WOULDBLOCK;
|
|
|
|
|
|
ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL,
|
|
|
- gfp_mask, flags);
|
|
|
+ gfp_mask, flags);
|
|
|
+ if (mem_cgroup_check_margin(mem_over_limit, csize))
|
|
|
+ return CHARGE_RETRY;
|
|
|
/*
|
|
|
- * try_to_free_mem_cgroup_pages() might not give us a full
|
|
|
- * picture of reclaim. Some pages are reclaimed and might be
|
|
|
- * moved to swap cache or just unmapped from the cgroup.
|
|
|
- * Check the limit again to see if the reclaim reduced the
|
|
|
- * current usage of the cgroup before giving up
|
|
|
+ * Even though the limit is exceeded at this point, reclaim
|
|
|
+ * may have been able to free some pages. Retry the charge
|
|
|
+ * before killing the task.
|
|
|
+ *
|
|
|
+ * Only for regular pages, though: huge pages are rather
|
|
|
+ * unlikely to succeed so close to the limit, and we fall back
|
|
|
+ * to regular pages anyway in case of failure.
|
|
|
*/
|
|
|
- if (ret || mem_cgroup_check_under_limit(mem_over_limit))
|
|
|
+ if (csize == PAGE_SIZE && ret)
|
|
|
return CHARGE_RETRY;
|
|
|
|
|
|
/*
|