[RHEL8,COMMIT] mm: add warning about high order allocations

Submitted by Konstantin Khorenko on May 7, 2020, 7:41 p.m.

Details

Message ID 202005071941.047JfjXj006899@finist_co8.work.ct
State New
Headers show

Patch hide | download patch | download mbox

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c40eea882001..898ec305032a 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -171,6 +171,11 @@  extern int unaligned_dump_stack;
 extern int no_unaligned_warning;
 #endif
 
+extern int warn_order;
+extern int proc_warn_high_order(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos);
+
+
 static bool virtual_ptr(void **ptr, void *base, size_t size, void *cur);
 #define sysctl_virtual(sysctl)							\
 int sysctl ## _virtual(struct ctl_table *table, int write,			\
@@ -1684,6 +1689,15 @@  static struct ctl_table vm_table[] = {
 		.extra2		= (void *)&mmap_rnd_compat_bits_max,
 	},
 #endif
+	{
+		.procname	= "warn_high_order",
+		.data		= &warn_order,
+		.maxlen		= sizeof(warn_order),
+		.mode		= 0644,
+		.proc_handler	= &proc_warn_high_order,
+		.extra1		= &zero,
+		.extra2		= &one_hundred,
+	},
 	{ }
 };
 
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index aadcc166bd0f..cbd6ee3ed01e 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4393,6 +4393,37 @@  static inline void finalise_ac(gfp_t gfp_mask, struct alloc_context *ac)
 					ac->high_zoneidx, ac->nodemask);
 }
 
+struct static_key warn_high_order_key = STATIC_KEY_INIT_FALSE;
+int warn_order = MAX_ORDER;
+
+int proc_warn_high_order(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	int ret;
+
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	if (!ret) {
+		smp_wmb();
+		static_key_slow_inc(&warn_high_order_key);
+	}
+
+	return ret;
+}
+
+static __always_inline void warn_high_order(int order, gfp_t gfp_mask)
+{
+	static atomic_t warn_count = ATOMIC_INIT(32);
+
+	if (static_key_false(&warn_high_order_key)) {
+		int tmp_warn_order = smp_load_acquire(&warn_order);
+
+		if (order >= tmp_warn_order && !(gfp_mask & __GFP_NOWARN))
+			WARN(atomic_dec_if_positive(&warn_count) >= 0,
+				"order %d >= %d, gfp 0x%x\n",
+				order, tmp_warn_order, gfp_mask);
+	}
+}
+
 /*
  * This is the 'heart' of the zoned buddy allocator.
  */
@@ -4407,6 +4438,9 @@  __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
 
 	gfp_mask &= gfp_allowed_mask;
 	alloc_mask = gfp_mask;
+
+	warn_high_order(order, gfp_mask);
+
 	if (!prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
 		return NULL;