Skip to contentSkip to conversation

Critical Page Pool

RFC PATCH In review Matthew Dobson 8 participants 26 messages Dec 14, 2005 – Dec 16, 2005

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

cover Critical Page Pool

Here is the latest version of the Critical Page Pool patches. Besides bugfixes, I've removed all the slab cleanup work from the series. Also, since one of the main questions about the patch series seems to revolve around how to appropriately size the pool, I've added some basic statistics about the critical page pool, viewable by reading /proc/sys/vm/critical_pages. The code now exports how many pages were requested, how many pages are currently in use, and the maximum number of pages that were ever in use.

The overall purpose of this patch series is to all a system administrator to reserve a number of pages in a 'critical pool' that is set aside for situations when the system is 'in emergency'. It is up to the individual administrator to determine when his/her system is 'in emergency'. This is not meant to (necessarily) anticipate OOM situations, though that is certainly one possible use. The purpose this was originally designed for is to allow the networking code to keep functioning despite the sytem losing its (potentially networked) swap device, and thus temporarily putting the system under exreme memory pressure.

Any comments about the code or the overall design are very welcome. Patches agaist 2.6.15-rc5.

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#1 lore raw

RFC PATCH 1/6 Create Critical Page Pool

Create the basic Critical Page Pool. Any allocation specifying __GFP_CRITICAL will, as a last resort before failing the allocation, try to get a page from the critical pool. For now, only singleton (order 0) pages are supported.

-Matt

Implement a Critical Page Pool:

* Write a number of pages into /proc/sys/vm/critical_pages
* These pages will be reserved for 'critical' allocations, signified by the __GFP_CRITICAL flag passed to an allocation request
* Reading /proc/sys/vm/critical_pages will give statistics about the critical page pool

Documentation/sysctl/vm.txt +10 -0
Index: linux-2.6.15-rc5+critical_pool/Documentation/sysctl/vm.txt
===================================================================
@@ -26,6 +26,7 @@ Currently, these files are in /proc/sys/
 - min_free_kbytes
 - laptop_mode
 - block_dump
+- critical_pages
 
 ==============================================================
 
@@ -102,3 +103,12 @@ This is used to force the Linux VM to ke
 of kilobytes free.  The VM uses this number to compute a pages_min
 value for each lowmem zone in the system.  Each lowmem zone gets 
 a number of reserved free pages based proportionally on its size.
+
+==============================================================
+
+critical_pages:
+
+This is used to force the Linux VM to reserve a pool of pages for
+emergency (__GFP_CRITICAL) allocations.  Allocations with this flag
+MUST succeed.
+The number written into this file is the number of pages to reserve.
include/linux/gfp.h +4 -1
Index: linux-2.6.15-rc5+critical_pool/include/linux/gfp.h
===================================================================
@@ -47,6 +47,7 @@ struct vm_area_struct;
 #define __GFP_ZERO	((__force gfp_t)0x8000u)/* Return zeroed page on success */
 #define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) /* Don't use emergency reserves */
 #define __GFP_HARDWALL   ((__force gfp_t)0x20000u) /* Enforce hardwall cpuset memory allocs */
+#define __GFP_CRITICAL	((__force gfp_t)0x40000u) /* Critical allocation. MUST succeed! */
 
 #define __GFP_BITS_SHIFT 20	/* Room for 20 __GFP_FOO bits */
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
@@ -55,7 +56,7 @@ struct vm_area_struct;
 #define GFP_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS| \
 			__GFP_COLD|__GFP_NOWARN|__GFP_REPEAT| \
 			__GFP_NOFAIL|__GFP_NORETRY|__GFP_NO_GROW|__GFP_COMP| \
-			__GFP_NOMEMALLOC|__GFP_HARDWALL)
+			__GFP_NOMEMALLOC|__GFP_HARDWALL|__GFP_CRITICAL)
 
 #define GFP_ATOMIC	(__GFP_HIGH)
 #define GFP_NOIO	(__GFP_WAIT)
@@ -64,6 +65,8 @@ struct vm_area_struct;
 #define GFP_USER	(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
 #define GFP_HIGHUSER	(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \
 			 __GFP_HIGHMEM)
+#define GFP_ATOMIC_CRIT	(GFP_ATOMIC | __GFP_CRITICAL)
+#define GFP_KERNEL_CRIT	(GFP_KERNEL | __GFP_CRITICAL)
 
 /* Flag - indicates that the buffer will be suitable for DMA.  Ignored on some
    platforms, used as appropriate on others */
include/linux/sysctl.h +1 -0
Index: linux-2.6.15-rc5+critical_pool/include/linux/sysctl.h
===================================================================
@@ -180,6 +180,7 @@ enum
 	VM_VFS_CACHE_PRESSURE=26, /* dcache/icache reclaim pressure */
 	VM_LEGACY_VA_LAYOUT=27, /* legacy/compatibility virtual address space layout */
 	VM_SWAP_TOKEN_TIMEOUT=28, /* default time for token time out */
+	VM_CRITICAL_PAGES=29,	/* # of pages to reserve for __GFP_CRITICAL allocs */
 };
 
 
kernel/sysctl.c +10 -0
Index: linux-2.6.15-rc5+critical_pool/kernel/sysctl.c
===================================================================
@@ -849,6 +849,16 @@ static ctl_table vm_table[] = {
 		.strategy	= &sysctl_jiffies,
 	},
 #endif
+	{
+		.ctl_name	= VM_CRITICAL_PAGES,
+		.procname	= "critical_pages",
+		.data		= &critical_pages,
+		.maxlen		= sizeof(critical_pages),
+		.mode		= 0644,
+		.proc_handler	= &critical_pages_sysctl_handler,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero,
+	},
 	{ .ctl_name = 0 }
 };
 
mm/page_alloc.c +163 -0

187 lines not on this page · raw message

include/linux/mmzone.h +2 -0
Index: linux-2.6.15-rc5+critical_pool/include/linux/mmzone.h
===================================================================
@@ -422,6 +422,8 @@ int min_free_kbytes_sysctl_handler(struc
 extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1];
 int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, struct file *,
 					void __user *, size_t *, loff_t *);
+int critical_pages_sysctl_handler(struct ctl_table *, int, struct file *,
+				  void __user *, size_t *, loff_t *);
 
 #include <linux/topology.h>
 /* Returns the number of the current Node. */
include/linux/mm.h +2 -0
Index: linux-2.6.15-rc5+critical_pool/include/linux/mm.h
===================================================================
@@ -32,6 +32,8 @@ extern int sysctl_legacy_va_layout;
 #define sysctl_legacy_va_layout 0
 #endif
 
+extern int critical_pages;
+
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
#6 lore raw

RFC PATCH 2/6 in_emergency Trigger

In reply to Matthew Dobson

Create the 'in_emergency' trigger, to allow userspace to turn access to the critical pool on and off. The rationale behind this is to ensure that the critical pool stays full for *actual* emergency situations, and isn't used for transient, low-mem situations.

-Matt

Create a userspace trigger: /proc/sys/vm/in_emergency that notifies the kernel that the system is in an emergency state, and allows the kernel to delve into the 'critical pool' to satisfy __GFP_CRITICAL allocations.

Documentation/sysctl/vm.txt +10 -0
Index: linux-2.6.15-rc5+critical_pool/Documentation/sysctl/vm.txt
===================================================================
@@ -27,6 +27,7 @@ Currently, these files are in /proc/sys/
 - laptop_mode
 - block_dump
 - critical_pages
+- in_emergency
 
 ==============================================================
 
@@ -112,3 +113,12 @@ This is used to force the Linux VM to re
 emergency (__GFP_CRITICAL) allocations.  Allocations with this flag
 MUST succeed.
 The number written into this file is the number of pages to reserve.
+
+==============================================================
+
+in_emergency:
+
+This is used to let the Linux VM know that userspace thinks that the system is
+in an emergency situation.
+Writing a non-zero value into this file tells the VM we *are* in an emergency
+situation & writing zero tells the VM we *are not* in an emergency situation.
include/linux/sysctl.h +1 -0
Index: linux-2.6.15-rc5+critical_pool/include/linux/sysctl.h
===================================================================
@@ -181,6 +181,7 @@ enum
 	VM_LEGACY_VA_LAYOUT=27, /* legacy/compatibility virtual address space layout */
 	VM_SWAP_TOKEN_TIMEOUT=28, /* default time for token time out */
 	VM_CRITICAL_PAGES=29,	/* # of pages to reserve for __GFP_CRITICAL allocs */
+	VM_IN_EMERGENCY=30,	/* tell the VM if we are/aren't in an emergency */
 };
 
 
kernel/sysctl.c +10 -0
Index: linux-2.6.15-rc5+critical_pool/kernel/sysctl.c
===================================================================
@@ -859,6 +859,16 @@ static ctl_table vm_table[] = {
 		.strategy	= &sysctl_intvec,
 		.extra1		= &zero,
 	},
+	{
+		.ctl_name	= VM_IN_EMERGENCY,
+		.procname	= "in_emergency",
+		.data		= &system_in_emergency,
+		.maxlen		= sizeof(system_in_emergency),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero,
+	},
 	{ .ctl_name = 0 }
 };
 
mm/page_alloc.c +16 -2
Index: linux-2.6.15-rc5+critical_pool/mm/page_alloc.c
===================================================================
@@ -53,6 +53,10 @@ unsigned long totalram_pages __read_most
 unsigned long totalhigh_pages __read_mostly;
 long nr_swap_pages;
 
+/* Is the sytem in an emergency situation? */
+int system_in_emergency = 0;
+EXPORT_SYMBOL(system_in_emergency);
+
 /* The number of pages to maintain in the critical page pool */
 int critical_pages = 0;
 
@@ -927,7 +931,7 @@ struct page * fastcall
 __alloc_pages(gfp_t gfp_mask, unsigned int order,
 		struct zonelist *zonelist)
 {
-	const gfp_t wait = gfp_mask & __GFP_WAIT;
+	gfp_t wait = gfp_mask & __GFP_WAIT;
 	struct zone **z;
 	struct page *page;
 	struct reclaim_state reclaim_state;
@@ -936,6 +940,16 @@ __alloc_pages(gfp_t gfp_mask, unsigned i
 	int alloc_flags;
 	int did_some_progress;
 
+	if (is_emergency_alloc(gfp_mask)) {
+		/*
+		 * If the system is 'in emergency' and this is a critical
+		 * allocation, then make sure we don't sleep
+		 */
+		gfp_mask &= ~__GFP_WAIT;
+		gfp_mask |= __GFP_HIGH;
+		wait = 0;
+	}
+
 	might_sleep_if(wait);
 
 restart:
@@ -1070,7 +1084,7 @@ nopage:
 	 * Rather than fail one of these allocations, take a page,
 	 * if there are any, from the critical pool.
 	 */
-	if ((gfp_mask & __GFP_CRITICAL) && !order) {
+	if (is_emergency_alloc(gfp_mask) && !order) {
 		page = get_critical_page(gfp_mask);
 		if (page)
 			goto got_pg;
include/linux/mm.h +6 -0
Index: linux-2.6.15-rc5+critical_pool/include/linux/mm.h
===================================================================
@@ -33,6 +33,12 @@ extern int sysctl_legacy_va_layout;
 #endif
 
 extern int critical_pages;
+extern int system_in_emergency;
+
+static inline int is_emergency_alloc(gfp_t gfpmask)
+{
+	return system_in_emergency && (gfpmask & __GFP_CRITICAL);
+}
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
#7 lore raw

RFC PATCH 3/6 Slab Prep: get/return_object

In reply to Matthew Dobson

Create 2 helper functions in mm/slab.c: get_object() and return_object(). These functions reduce some existing duplicated code in the slab allocator and will be used when adding Critical Page Pool support to the slab allocator.

-Matt

Create two helper functions: get_object_from_slab() & return_object_to_slab(). Use these two helper function to replace duplicated code in mm/slab.c

These functions will also be reused by a later patch in this series.

mm/slab.c +40 -37
Index: linux-2.6.15-rc5+critical_pool/mm/slab.c
===================================================================
@@ -2140,6 +2140,42 @@ static void kmem_flagcheck(kmem_cache_t 
 	}
 }
 
+static void *get_object(kmem_cache_t *cachep, struct slab *slabp, int nodeid)
+{
+	void *objp = slabp->s_mem + (slabp->free * cachep->objsize);
+	kmem_bufctl_t next;
+
+	slabp->inuse++;
+	next = slab_bufctl(slabp)[slabp->free];
+#if DEBUG
+	slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
+	WARN_ON(slabp->nodeid != nodeid);
+#endif
+	slabp->free = next;
+
+	return objp;
+}
+
+static void return_object(kmem_cache_t *cachep, struct slab *slabp, void *objp,
+			  int nodeid)
+{
+	unsigned int objnr = (objp - slabp->s_mem) / cachep->objsize;
+
+#if DEBUG
+	/* Verify that the slab belongs to the intended node */
+	WARN_ON(slabp->nodeid != nodeid);
+
+	if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {
+		printk(KERN_ERR "slab: double free detected in cache "
+		       "'%s', objp %p\n", cachep->name, objp);
+		BUG();
+	}
+#endif
+	slab_bufctl(slabp)[objnr] = slabp->free;
+	slabp->free = objnr;
+	slabp->inuse--;
+}
+
 static void set_slab_attr(kmem_cache_t *cachep, struct slab *slabp, void *objp)
 {
 	int i;
@@ -2418,22 +2454,12 @@ retry:
 		check_slabp(cachep, slabp);
 		check_spinlock_acquired(cachep);
 		while (slabp->inuse < cachep->num && batchcount--) {
-			kmem_bufctl_t next;
 			STATS_INC_ALLOCED(cachep);
 			STATS_INC_ACTIVE(cachep);
 			STATS_SET_HIGH(cachep);
 
-			/* get obj pointer */
-			ac->entry[ac->avail++] = slabp->s_mem +
-				slabp->free*cachep->objsize;
-
-			slabp->inuse++;
-			next = slab_bufctl(slabp)[slabp->free];
-#if DEBUG
-			slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
-			WARN_ON(numa_node_id() != slabp->nodeid);
-#endif
-		       	slabp->free = next;
+			ac->entry[ac->avail++] = get_object(cachep, slabp,
+							    numa_node_id());
 		}
 		check_slabp(cachep, slabp);
 
@@ -2565,7 +2591,6 @@ static void *__cache_alloc_node(kmem_cac
  	struct slab *slabp;
  	struct kmem_list3 *l3;
  	void *obj;
- 	kmem_bufctl_t next;
  	int x;
 
  	l3 = cachep->nodelists[nodeid];
@@ -2591,14 +2616,7 @@ retry:
 
  	BUG_ON(slabp->inuse == cachep->num);
 
- 	/* get obj pointer */
- 	obj =  slabp->s_mem + slabp->free*cachep->objsize;
- 	slabp->inuse++;
- 	next = slab_bufctl(slabp)[slabp->free];
-#if DEBUG
- 	slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
-#endif
- 	slabp->free = next;
+	obj = get_object(cachep, slabp, nodeid);
  	check_slabp(cachep, slabp);
  	l3->free_objects--;
  	/* move slabp to correct slabp list: */
@@ -2637,29 +2655,14 @@ static void free_block(kmem_cache_t *cac
 	for (i = 0; i < nr_objects; i++) {
 		void *objp = objpp[i];
 		struct slab *slabp;
-		unsigned int objnr;
 
 		slabp = page_get_slab(virt_to_page(objp));
 		l3 = cachep->nodelists[node];
 		list_del(&slabp->list);
-		objnr = (objp - slabp->s_mem) / cachep->objsize;
 		check_spinlock_acquired_node(cachep, node);
 		check_slabp(cachep, slabp);
-
-#if DEBUG
-		/* Verify that the slab belongs to the intended node */
-		WARN_ON(slabp->nodeid != node);
-
-		if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {
-			printk(KERN_ERR "slab: double free detected in cache "
-					"'%s', objp %p\n", cachep->name, objp);
-			BUG();
-		}
-#endif
-		slab_bufctl(slabp)[objnr] = slabp->free;
-		slabp->free = objnr;
+		return_object(cachep, slabp, objp, node);
 		STATS_DEC_ACTIVE(cachep);
-		slabp->inuse--;
 		l3->free_objects++;
 		check_slabp(cachep, slabp);
 
#10 lore raw

RFC PATCH 4/6 Slab Prep: slab_destruct()

In reply to Matthew Dobson

Create a helper function for slab_destroy() called slab_destruct(). Remove some ifdefs inside functions and generally make the slab destroying code more readable prior to slab support for the Critical Page Pool.

-Matt

Create a helper function, slab_destruct(), called from slab_destroy(). This makes slab_destroy() smaller and more readable, and moves ifdefs outside the function body.

mm/slab.c +19 -7
Index: linux-2.6.15-rc5+critical_pool/mm/slab.c
===================================================================
@@ -1401,15 +1401,13 @@ static void check_poison_obj(kmem_cache_
 }
 #endif
 
-/* Destroy all the objs in a slab, and release the mem back to the system.
- * Before calling the slab must have been unlinked from the cache.
- * The cache-lock is not held/needed.
+#if DEBUG
+/**
+ * slab_destruct - call the registered destructor for each object in
+ *      a slab that is to be destroyed.
  */
-static void slab_destroy (kmem_cache_t *cachep, struct slab *slabp)
+static void slab_destruct(kmem_cache_t *cachep, struct slab *slabp)
 {
-	void *addr = slabp->s_mem - slabp->colouroff;
-
-#if DEBUG
 	int i;
 	for (i = 0; i < cachep->num; i++) {
 		void *objp = slabp->s_mem + cachep->objsize * i;
@@ -1435,7 +1433,10 @@ static void slab_destroy (kmem_cache_t *
 		if (cachep->dtor && !(cachep->flags & SLAB_POISON))
 			(cachep->dtor)(objp+obj_dbghead(cachep), cachep, 0);
 	}
+}
 #else
+static void slab_destruct(kmem_cache_t *cachep, struct slab *slabp)
+{
 	if (cachep->dtor) {
 		int i;
 		for (i = 0; i < cachep->num; i++) {
@@ -1443,8 +1444,19 @@ static void slab_destroy (kmem_cache_t *
 			(cachep->dtor)(objp, cachep, 0);
 		}
 	}
+}
 #endif
 
+/**
+ * Destroy all the objs in a slab, and release the mem back to the system.
+ * Before calling the slab must have been unlinked from the cache.
+ * The cache-lock is not held/needed.
+ */
+static void slab_destroy(kmem_cache_t *cachep, struct slab *slabp)
+{
+	void *addr = slabp->s_mem - slabp->colouroff;
+
+	slab_destruct(cachep, slabp);
 	if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) {
 		struct slab_rcu *slab_rcu;
 
#13 lore raw

RFC PATCH 5/6 Slab Prep: Move cache_grow()

In reply to Matthew Dobson

Move cache_grow() a few lines further down in mm/slab.c to gain access to a couple debugging functions that will be used by the next patch. Also, rename a goto label and fixup a couple comments.

-Matt

Move cache_grow() below some debugging function definitions, so those debugging functions can be inserted into cache_grow() by the next patch without needing forward declarations.

Also, do a few small cleanups:
Tidy up a few comments
Rename a label to something readable

mm/slab.c +84 -90
Index: linux-2.6.15-rc5+critical_pool/mm/slab.c
===================================================================
@@ -2203,96 +2203,6 @@ static void set_slab_attr(kmem_cache_t *
 	} while (--i);
 }
 
-/*
- * Grow (by 1) the number of slabs within a cache.  This is called by
- * kmem_cache_alloc() when there are no active objs left in a cache.
- */
-static int cache_grow(kmem_cache_t *cachep, gfp_t flags, int nodeid)
-{
-	struct slab	*slabp;
-	void		*objp;
-	size_t		 offset;
-	gfp_t	 	 local_flags;
-	unsigned long	 ctor_flags;
-	struct kmem_list3 *l3;
-
-	/* Be lazy and only check for valid flags here,
- 	 * keeping it out of the critical path in kmem_cache_alloc().
-	 */
-	if (flags & ~(SLAB_DMA|SLAB_LEVEL_MASK|SLAB_NO_GROW))
-		BUG();
-	if (flags & SLAB_NO_GROW)
-		return 0;
-
-	ctor_flags = SLAB_CTOR_CONSTRUCTOR;
-	local_flags = (flags & SLAB_LEVEL_MASK);
-	if (!(local_flags & __GFP_WAIT))
-		/*
-		 * Not allowed to sleep.  Need to tell a constructor about
-		 * this - it might need to know...
-		 */
-		ctor_flags |= SLAB_CTOR_ATOMIC;
-
-	/* About to mess with non-constant members - lock. */
-	check_irq_off();
-	spin_lock(&cachep->spinlock);
-
-	/* Get colour for the slab, and cal the next value. */
-	offset = cachep->colour_next;
-	cachep->colour_next++;
-	if (cachep->colour_next >= cachep->colour)
-		cachep->colour_next = 0;
-	offset *= cachep->colour_off;
-
-	spin_unlock(&cachep->spinlock);
-
-	check_irq_off();
-	if (local_flags & __GFP_WAIT)
-		local_irq_enable();
-
-	/*
-	 * The test for missing atomic flag is performed here, rather than
-	 * the more obvious place, simply to reduce the critical path length
-	 * in kmem_cache_alloc(). If a caller is seriously mis-behaving they
-	 * will eventually be caught here (where it matters).
-	 */
-	kmem_flagcheck(cachep, flags);
-
-	/* Get mem for the objs.
-	 * Attempt to allocate a physical page from 'nodeid',
-	 */
-	if (!(objp = kmem_getpages(cachep, flags, nodeid)))
-		goto failed;
-
-	/* Get slab management. */
-	if (!(slabp = alloc_slabmgmt(cachep, objp, offset, local_flags)))
-		goto opps1;
-
-	slabp->nodeid = nodeid;
-	set_slab_attr(cachep, slabp, objp);
-
-	cache_init_objs(cachep, slabp, ctor_flags);
-
-	if (local_flags & __GFP_WAIT)
-		local_irq_disable();
-	check_irq_off();
-	l3 = cachep->nodelists[nodeid];
-	spin_lock(&l3->list_lock);
-
-	/* Make slab active. */
-	list_add_tail(&slabp->list, &(l3->slabs_free));
-	STATS_INC_GROWN(cachep);
-	l3->free_objects += cachep->num;
-	spin_unlock(&l3->list_lock);
-	return 1;
-opps1:
-	kmem_freepages(cachep, objp);
-failed:
-	if (local_flags & __GFP_WAIT)
-		local_irq_disable();
-	return 0;
-}
-
 #if DEBUG
 
 /*
@@ -2414,6 +2324,90 @@ bad:
 #define check_slabp(x,y) do { } while(0)
 #endif
 
+/**
+ * Grow (by 1) the number of slabs within a cache.  This is called by
+ * kmem_cache_alloc() when there are no active objs left in a cache.
+ */
+static int cache_grow(kmem_cache_t *cachep, gfp_t flags, int nodeid)
+{
+	struct slab *slabp;
+	void *objp;
+	size_t offset;
+	gfp_t local_flags;
+	unsigned long ctor_flags;
+	struct kmem_list3 *l3;
+
+	/*
+	 * Be lazy and only check for valid flags here,
+	 * keeping it out of the critical path in kmem_cache_alloc().
+	 */
+	if (flags & ~(SLAB_DMA|SLAB_LEVEL_MASK|SLAB_NO_GROW))
+		BUG();
+	if (flags & SLAB_NO_GROW)
+		return 0;
+
+	ctor_flags = SLAB_CTOR_CONSTRUCTOR;
+	local_flags = (flags & SLAB_LEVEL_MASK);
+	if (!(local_flags & __GFP_WAIT))
+		/* The constructor might need to know it can't sleep */
+		ctor_flags |= SLAB_CTOR_ATOMIC;
+
+	/* About to mess with non-constant members - lock. */
+	check_irq_off();
+	spin_lock(&cachep->spinlock);
+	/* Get colour for the slab, and calculate the next value. */
+	offset = cachep->colour_next;
+	cachep->colour_next++;
+	if (cachep->colour_next >= cachep->colour)
+		cachep->colour_next = 0;
+	offset *= cachep->colour_off;
+	/* done...  Unlock. */
+	spin_unlock(&cachep->spinlock);
+
+	check_irq_off();
+	if (local_flags & __GFP_WAIT)
+		local_irq_enable();
+
+	/*
+	 * Ensure caller isn't asking for DMA memory if the slab wasn't created
+	 * with the SLAB_DMA flag.
+	 * Also ensure the caller *is* asking for DMA memory if the slab was
+	 * created with the SLAB_DMA flag.
+	 */
+	kmem_flagcheck(cachep, flags);
+
+	/* Get memory for the objects by allocating a page from 'nodeid'. */
+	if (!(objp = kmem_getpages(cachep, flags, nodeid)))
+		goto failed;
+
+	/* Get slab management. */
+	if (!(slabp = alloc_slabmgmt(cachep, objp, offset, local_flags)))
+		goto failed_freepages;
+
+	slabp->nodeid = nodeid;
+	set_slab_attr(cachep, slabp, objp);
+	cache_init_objs(cachep, slabp, ctor_flags);
+
+	if (local_flags & __GFP_WAIT)
+		local_irq_disable();
+	check_irq_off();
+	l3 = cachep->nodelists[nodeid];
+	spin_lock(&l3->list_lock);
+
+	/* Make slab active. */
+	list_add_tail(&slabp->list, &(l3->slabs_free));
+	STATS_INC_GROWN(cachep);
+	l3->free_objects += cachep->num;
+	spin_unlock(&l3->list_lock);
+	return 1;
+failed_freepages:
+	kmem_freepages(cachep, objp);
+failed:
+	if (local_flags & __GFP_WAIT)
+		local_irq_disable();
+	return 0;
+}
+
 static void *cache_alloc_refill(kmem_cache_t *cachep, gfp_t flags)
 {
 	int batchcount;
#14 lore raw

RFC PATCH 6/6 Critical Page Pool: Slab Support

In reply to Matthew Dobson

Finally, add support for the Critical Page Pool to the Slab Allocator. We need the slab allocator to be at least marginally aware of the existence of critical pages, or else we leave open the possibility of non-critical slab allocations stealing objects from 'critical' slabs. We add a separate, node-unspecific list to kmem_cache_t called slabs_crit. We keep all partial and full critical slabs on this list. We don't keep empty critical slabs around, in the interest of giving this memory back to the VM ASAP in what is typically a high memory pressure situation.

-Matt

Modify the Slab Allocator to support the addition of a Critical Pool to the VM. What we want is to ensure that if a cache is allocated a new slab page from the Critical Pool during an Emergency situation, that only other __GFP_CRITICAL allocations are satisfied from that slab.

mm/slab.c +102 -21
Index: linux-2.6.15-rc5+critical_pool/mm/slab.c
===================================================================
@@ -221,8 +221,9 @@ struct slab {
 	unsigned long		colouroff;
 	void			*s_mem;		/* including colour offset */
 	unsigned int		inuse;		/* num of objs active in slab */
-	kmem_bufctl_t		free;
+	unsigned short		critical;	/* is this an critical slab? */
 	unsigned short          nodeid;
+	kmem_bufctl_t		free;
 };
 
 /*
@@ -395,6 +396,9 @@ struct kmem_cache {
 	unsigned int		slab_size;
 	unsigned int		dflags;		/* dynamic flags */
 
+	/* list of critical slabs for this cache */
+	struct list_head	slabs_crit;
+
 	/* constructor func */
 	void (*ctor)(void *, kmem_cache_t *, unsigned long);
 
@@ -1770,6 +1774,7 @@ next:
 		cachep->gfpflags |= GFP_DMA;
 	spin_lock_init(&cachep->spinlock);
 	cachep->objsize = size;
+	INIT_LIST_HEAD(&cachep->slabs_crit);
 
 	if (flags & CFLGS_OFF_SLAB)
 		cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);
@@ -2086,6 +2091,7 @@ static struct slab* alloc_slabmgmt(kmem_
 	slabp->inuse = 0;
 	slabp->colouroff = colour_off;
 	slabp->s_mem = objp+colour_off;
+	slabp->critical = 0;
 
 	return slabp;
 }
@@ -2161,7 +2167,8 @@ static void *get_object(kmem_cache_t *ca
 	next = slab_bufctl(slabp)[slabp->free];
 #if DEBUG
 	slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
-	WARN_ON(slabp->nodeid != nodeid);
+	if (nodeid >= 0)
+		WARN_ON(slabp->nodeid != nodeid);
 #endif
 	slabp->free = next;
 
@@ -2175,7 +2182,8 @@ static void return_object(kmem_cache_t *
 
 #if DEBUG
 	/* Verify that the slab belongs to the intended node */
-	WARN_ON(slabp->nodeid != nodeid);
+	if (nodeid >= 0)
+		WARN_ON(slabp->nodeid != nodeid);
 
 	if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {
 		printk(KERN_ERR "slab: double free detected in cache "
@@ -2324,18 +2332,64 @@ bad:
 #define check_slabp(x,y) do { } while(0)
 #endif
 
+static inline int is_critical_object(void *obj)
+{
+	struct slab *slabp;
+
+	if (!obj)
+		return 0;
+
+	slabp = page_get_slab(virt_to_page(obj));
+	return slabp->critical;
+}
+
+static inline void *get_critical_object(kmem_cache_t *cachep, gfp_t flags)
+{
+	struct slab *slabp;
+	void *objp = NULL;
+
+	spin_lock(&cachep->spinlock);
+	/* search for any partially free critical slabs */
+	if (!list_empty(&cachep->slabs_crit))
+		list_for_each_entry(slabp, &cachep->slabs_crit, list)
+			if (slabp->free != BUFCTL_END) {
+				objp = get_object(cachep, slabp, -1);
+				check_slabp(cachep, slabp);
+				break;
+			}
+	spin_unlock(&cachep->spinlock);
+
+	return objp;
+}
+
+static inline void free_critical_object(kmem_cache_t *cachep, void *objp)
+{
+	struct slab *slabp = page_get_slab(virt_to_page(objp));
+
+	check_slabp(cachep, slabp);
+	return_object(cachep, slabp, objp, -1);
+	check_slabp(cachep, slabp);
+
+	if (!slabp->inuse) {
+		BUG_ON(cachep->flags & SLAB_DESTROY_BY_RCU);
+
+		list_del(&slabp->list);
+		slab_destroy(cachep, slabp);
+	}
+}
+
 /**
  * Grow (by 1) the number of slabs within a cache.  This is called by
  * kmem_cache_alloc() when there are no active objs left in a cache.
  */
-static int cache_grow(kmem_cache_t *cachep, gfp_t flags, int nodeid)
+static void *cache_grow(kmem_cache_t *cachep, gfp_t flags, int nodeid)
 {
 	struct slab *slabp;
 	void *objp;
 	size_t offset;
 	gfp_t local_flags;
 	unsigned long ctor_flags;
-	struct kmem_list3 *l3;
+	int critical = is_emergency_alloc(flags) && !cachep->gfporder;
 
 	/*
 	 * Be lazy and only check for valid flags here,
@@ -2344,7 +2398,14 @@ static int cache_grow(kmem_cache_t *cach
 	if (flags & ~(SLAB_DMA|SLAB_LEVEL_MASK|SLAB_NO_GROW))
 		BUG();
 	if (flags & SLAB_NO_GROW)
-		return 0;
+		return NULL;
+
+	/*
+	 * If we are in an emergency situation and this is a 'critical' alloc,
+	 * see if we can get an object from an existing critical slab first.
+	 */
+	if (critical && (objp = get_critical_object(cachep, flags)))
+		return objp;
 
 	ctor_flags = SLAB_CTOR_CONSTRUCTOR;
 	local_flags = (flags & SLAB_LEVEL_MASK);
@@ -2391,21 +2452,30 @@ static int cache_grow(kmem_cache_t *cach
 	if (local_flags & __GFP_WAIT)
 		local_irq_disable();
 	check_irq_off();
-	l3 = cachep->nodelists[nodeid];
-	spin_lock(&l3->list_lock);
 
 	/* Make slab active. */
-	list_add_tail(&slabp->list, &(l3->slabs_free));
 	STATS_INC_GROWN(cachep);
-	l3->free_objects += cachep->num;
-	spin_unlock(&l3->list_lock);
-	return 1;
+	if (!critical) {
+		struct kmem_list3 *l3 = cachep->nodelists[nodeid];
+		spin_lock(&l3->list_lock);
+		list_add_tail(&slabp->list, &l3->slabs_free);
+		l3->free_objects += cachep->num;
+		spin_unlock(&l3->list_lock);
+	} else {
+		slabp->critical = 1;
+		spin_lock(&cachep->spinlock);
+		list_add_tail(&slabp->list, &cachep->slabs_crit);
+		spin_unlock(&cachep->spinlock);
+		objp = get_object(cachep, slabp, -1);
+		check_slabp(cachep, slabp);
+	}
+	return objp;
 failed_freepages:
 	kmem_freepages(cachep, objp);
 failed:
 	if (local_flags & __GFP_WAIT)
 		local_irq_disable();
-	return 0;
+	return NULL;
 }
 
 static void *cache_alloc_refill(kmem_cache_t *cachep, gfp_t flags)
@@ -2483,15 +2553,18 @@ alloc_done:
 	spin_unlock(&l3->list_lock);
 
 	if (unlikely(!ac->avail)) {
-		int x;
-		x = cache_grow(cachep, flags, numa_node_id());
+		void *obj = cache_grow(cachep, flags, numa_node_id());
+
+		/* critical objects don't "grow" the slab, just return 'obj' */
+		if (is_critical_object(obj))
+			return obj;
 
-		// cache_grow can reenable interrupts, then ac could change.
+		/* cache_grow can reenable interrupts, then ac could change. */
 		ac = ac_data(cachep);
-		if (!x && ac->avail == 0)	// no objects in sight? abort
+		if (!obj && ac->avail == 0) /* No objects in sight?  Abort.  */
 			return NULL;
 
-		if (!ac->avail)		// objects refilled by interrupt?
+		if (!ac->avail)		/* objects refilled by interrupt?    */
 			goto retry;
 	}
 	ac->touched = 1;
@@ -2597,7 +2670,6 @@ static void *__cache_alloc_node(kmem_cac
  	struct slab *slabp;
  	struct kmem_list3 *l3;
  	void *obj;
- 	int x;
 
  	l3 = cachep->nodelists[nodeid];
  	BUG_ON(!l3);
@@ -2639,11 +2711,15 @@ retry:
 
 must_grow:
  	spin_unlock(&l3->list_lock);
- 	x = cache_grow(cachep, flags, nodeid);
+	obj = cache_grow(cachep, flags, nodeid);
 
- 	if (!x)
+	if (!obj)
  		return NULL;
 
+	/* critical objects don't "grow" the slab, just return 'obj' */
+	if (is_critical_object(obj))
+		goto done;
+
  	goto retry;
 done:
  	return obj;
@@ -2758,6 +2834,11 @@ static inline void __cache_free(kmem_cac
 	check_irq_off();
 	objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
 
+	if (is_critical_object(objp)) {
+		free_critical_object(cachep, objp);
+		return;
+	}
+
 	/* Make sure we are not freeing a object from another
 	 * node to the array cache on this cpu.
 	 */
#8 lore raw

Re: [RFC][PATCH 3/6] Slab Prep: get/return_object

In reply to Matthew Dobson

Hi Matt,

Quoted 3 lines · Matthew Dobson

Create 2 helper functions in mm/slab.c: get_object() and return_object(). These functions reduce some existing duplicated code in the slab allocator and will be used when adding Critical Page Pool support to the slab allocator.

May I suggest different naming, slab_get_obj and slab_put_obj ?

Pekka

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw

Re: [RFC][PATCH 4/6] Slab Prep: slab_destruct()

In reply to Matthew Dobson
Quoted 3 lines · Matthew Dobson

Create a helper function for slab_destroy() called slab_destruct(). Remove some ifdefs inside functions and generally make the slab destroying code more readable prior to slab support for the Critical Page Pool.

Looks good. How about calling it slab_destroy_objs instead?

Pekka

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw
In reply to Matthew Dobson

Hi!

Quoted 9 lines

The overall purpose of this patch series is to all a system administrator to reserve a number of pages in a 'critical pool' that is set aside for situations when the system is 'in emergency'. It is up to the individual administrator to determine when his/her system is 'in emergency'. This is not meant to (necessarily) anticipate OOM situations, though that is certainly one possible use. The purpose this was originally designed for is to allow the networking code to keep functioning despite the sytem losing its (potentially networked) swap device, and thus temporarily putting the system under exreme memory pressure.

I don't see how this can ever work.

How can _userspace_ know about what allocations are critical to the kernel?!

And as you noticed, it does not work for your original usage case, because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

If you want few emergency pages for some strange hack you are doing (swapping over network?), just put swap into ramdisk and swapon() it when you are in emergency, or use memory hotplug and plug few more gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.
Pavel

Signature · 7 lines
Thanks, Sharp!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#2 lore raw

Re: [RFC][PATCH 1/6] Create Critical Page Pool

In reply to Matthew Dobson

Hi Matthew,

Quoted 4 lines · Matthew Dobson

Create the basic Critical Page Pool. Any allocation specifying __GFP_CRITICAL will, as a last resort before failing the allocation, try to get a page from the critical pool. For now, only singleton (order 0) pages are supported.

Hmm sorry, but this design looks wrong to me. Since the caller has to use __GFP_CRITICAL anyway, why don't you build this critical pool _outside_ the page allocator exactly like the mempool does?

Then you will also get an huge advantage, that is allowing to create more than one critical pool without having to add a __GFP_CRITICAL2 next month.

So IMHO if something you should create something like a mempool (if the mempool isn't good enough already for your usage), so more subsystems can register their critical pools. Call it criticalpool.c or similar but I wouldn't mess with __GFP_* and page_alloc.c, and the sysctl should be in the user subsystem, not global.

Or perhaps you can share the mempool code and extend the mempool API to refill itself internally automatically as soon as pages are being released.

You may still need a single hook in the __free_pages path, to refill pools transparently from any freeing (not only the freeing of your subsystem) but such an hook is acceptable. You may need to set priorities in the criticalpool.c api as well to choose which pool to refill first, or if to refill them in round robin when they've the same priority.

I would touch page_alloc.c only with regard of the prioritized pool refilling with a registration hook and I would definitely not use a global pool and I wouldn't use __GFP_ bitflag for it.

Then each slab will be allowed to have its criticalpool too, then, not a global one. A global one driven by the __GFP_CRITICAL flag will quickly become useless as soon as you've more than one subsystem using it, plus it unnecessairly mess with page_alloc.c APIs where the only thing you care about is to catch the freeing operation with a hook.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#16 lore raw
In reply to Pavel Machek
Quoted 3 lines · Pavel Machek

because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Yes, a global pool isn't really useful. A per-subsystem pool would be more reasonable...

Quoted 2 lines

gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Agreed, the current design of the patch can't be used right.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw
Quoted 7 lines · Andrea Arcangeli
Quoted 3 lines · Pavel Machek

because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Yes, a global pool isn't really useful. A per-subsystem pool would be more reasonable...

The whole extra critical level seems dubious in itself. In 2.0/2.2 days there were a set of patches that just dropped incoming memory on sockets when the memory was tight unless they were marked as critical (ie NFS swap). It worked rather well. The rest of the changes beyond that seem excessive.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#3 lore raw

Re: [RFC][PATCH 1/6] Create Critical Page Pool

In reply to Matthew Dobson
Quoted 4 lines · Matthew Dobson

Create the basic Critical Page Pool. Any allocation specifying __GFP_CRITICAL will, as a last resort before failing the allocation, try to get a page from the critical pool. For now, only singleton (order 0) pages are supported.

How are you going to limit the number of GFP_CRITICAL allocations to something smaller than the number of pages in the pool ?

Unless you can do that, all guarantees are off...

Signature · 7 lines
All Rights Reversed

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#22 lore raw
In reply to Pavel Machek
Quoted 14 lines · Pavel Machek

Hi!

Quoted 9 lines

The overall purpose of this patch series is to all a system administrator to reserve a number of pages in a 'critical pool' that is set aside for situations when the system is 'in emergency'. It is up to the individual administrator to determine when his/her system is 'in emergency'. This is not meant to (necessarily) anticipate OOM situations, though that is certainly one possible use. The purpose this was originally designed for is to allow the networking code to keep functioning despite the sytem losing its (potentially networked) swap device, and thus temporarily putting the system under exreme memory pressure.

Quoted 4 lines

I don't see how this can ever work.

How can _userspace_ know about what allocations are critical to the kernel?!

Well, it isn't userspace that is determining *which* allocations are critical to the kernel. That is statically determined at compile time by using the flag __GFP_CRITICAL on specific *kernel* allocations. Sridhar, cc'd on this mail, has a set of patches that sprinkle the __GFP_CRITICAL flag throughout the networking code to take advantage of this pool. Userspace is in charge of determining *when* we're in an emergency situation, and should thus use the critical pool, but not *which* allocations are critical to surviving this emergency situation.

Quoted 4 lines

And as you noticed, it does not work for your original usage case, because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Well, I never suggested it didn't work for my original usage case. The discussion we had is that it would be incredibly difficult to 100% iron-clad guarantee that the pool would NEVER run out of pages. But we can size the pool, especially given a decent workload approximation, so as to make failure far less likely.

Quoted 5 lines

If you want few emergency pages for some strange hack you are doing (swapping over network?), just put swap into ramdisk and swapon() it when you are in emergency, or use memory hotplug and plug few more gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Well, that's basically the point of posting these patches as an RFC. I'm not quite so delusional as to think they're going to get picked up right now. I was, however, hoping for feedback to figure out how to design infrastructure that *can* be used right, as well as trying to find other potential users of such a feature.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw
In reply to Andrea Arcangeli
Quoted 9 lines · Andrea Arcangeli
Quoted 3 lines · Pavel Machek

because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Yes, a global pool isn't really useful. A per-subsystem pool would be more reasonable...

Which is an idea that I toyed with, as well. The problem that I ran into is how to tag an allocation as belonging to a specific subsystem. For example, in our code we need networking to use the critical pool. How do we let __alloc_pages() know what allocations belong to networking? Networking needs named slab allocations, kmalloc allocations, and whole page allocations to function. Should each subsystem get it's own GFP flag (GFP_NETWORKING, GFP_SCSI, GFP_SOUND, GFP_TERMINAL, ad nauseum)? Should we create these pools dynamically and pass a reference to which pool each specific allocation uses (thus adding a parameter to all memory allocation functions in the kernel)? I realize that per-subsystem pools would be better, but I thought about this for a while and couldn't come up with a reasonable way to do it.

Quoted 5 lines
Quoted 2 lines

gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Agreed, the current design of the patch can't be used right.

Well, it can for our use, but I recognize that isn't going to be a huge selling point! :) As I mentioned in my reply to Pavel, I'd really like to find a way to design something that WOULD be generally useful.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw

Re: [RFC][PATCH 1/6] Create Critical Page Pool

In reply to Rik van Riel
Quoted 9 lines · Rik van Riel
Quoted 4 lines · Matthew Dobson

Create the basic Critical Page Pool. Any allocation specifying __GFP_CRITICAL will, as a last resort before failing the allocation, try to get a page from the critical pool. For now, only singleton (order 0) pages are supported.

Quoted 3 lines

How are you going to limit the number of GFP_CRITICAL allocations to something smaller than the number of pages in the pool ?

We can't.

Quoted 1 line

Unless you can do that, all guarantees are off...

Well, I was careful not to use the word guarantee in my post. ;) The idea is not to offer a 100% guarantee that the pool will never be exhausted. The idea is to offer a pool that, sized appropriately, offers a very good chance of surviving your emergency situation. The definition of what is a critical allocation and what the emergency situation is left intentionally somewhat vague, so as to offer more flexibility. For our use, certain networking allocations are critical and our emergency situation is a 2 minute window of potential exreme memory pressure. For others it could be something completely different, but the expectation is that the emergency situation would be of a finite time, since the pool is a fixed size.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#9 lore raw

Re: [RFC][PATCH 3/6] Slab Prep: get/return_object

In reply to Pekka Enberg
Quoted 4 lines · Pekka Enberg

Hi Matt,

On 12/14/05, Matthew Dobson <[email protected]> wrote:

Quoted 8 lines
Quoted 3 lines

Create 2 helper functions in mm/slab.c: get_object() and return_object(). These functions reduce some existing duplicated code in the slab allocator and will be used when adding Critical Page Pool support to the slab allocator.

May I suggest different naming, slab_get_obj and slab_put_obj ?

Pekka

Sure. Those sound much better than mine. :)

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw

Re: [RFC][PATCH 4/6] Slab Prep: slab_destruct()

In reply to Pekka Enberg
Quoted 10 lines · Pekka Enberg
Quoted 3 lines · Matthew Dobson

Create a helper function for slab_destroy() called slab_destruct(). Remove some ifdefs inside functions and generally make the slab destroying code more readable prior to slab support for the Critical Page Pool.

Looks good. How about calling it slab_destroy_objs instead?

Pekka

I called it slab_destruct() because it's the part of the old slab_destroy() that called the slab destructor to destroy the slab's objects. slab_destroy_objs() is reasonable as well, though, and I can live with that.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#18 lore raw
In reply to Alan Cox
Quoted 13 lines · Alan Cox
Quoted 8 lines · Andrea Arcangeli
Quoted 3 lines · Pavel Machek

because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Yes, a global pool isn't really useful. A per-subsystem pool would be more reasonable...

Quoted 5 lines

The whole extra critical level seems dubious in itself. In 2.0/2.2 days there were a set of patches that just dropped incoming memory on sockets when the memory was tight unless they were marked as critical (ie NFS swap). It worked rather well. The rest of the changes beyond that seem excessive.

Actually, Sridhar's code (mentioned earlier in this thread) *does* drop incoming packets that are not 'critical', but unfortunately you need to completely copy the packet into kernel memory before you can do any processing on it to determine whether or not it's 'critical', and thus accept or reject it. If network traffic is coming in at a good clip and the system is already under memory pressure, it's going to be difficult to receive all these packets, which was the inspiration for this patchset.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#19 lore raw
Quoted 2 lines · Matthew Dobson

Actually, Sridhar's code (mentioned earlier in this thread) *does* drop incoming packets that are not 'critical', but unfortunately you need to

I realise that but if you look at the previous history in 2.0 and 2.2 this was all that was ever needed. It thus begs the question why all the extra support and logic this time around ?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#5 lore raw

Re: [RFC][PATCH 1/6] Create Critical Page Pool

In reply to Matthew Dobson
Quoted 19 lines · Matthew Dobson
Quoted 9 lines · Rik van Riel
Quoted 4 lines · Matthew Dobson

Create the basic Critical Page Pool. Any allocation specifying __GFP_CRITICAL will, as a last resort before failing the allocation, try to get a page from the critical pool. For now, only singleton (order 0) pages are supported.

Quoted 3 lines

How are you going to limit the number of GFP_CRITICAL allocations to something smaller than the number of pages in the pool ?

We can't.

Quoted 1 line

Unless you can do that, all guarantees are off...

Quoted 10 lines

Well, I was careful not to use the word guarantee in my post. ;) The idea is not to offer a 100% guarantee that the pool will never be exhausted. The idea is to offer a pool that, sized appropriately, offers a very good chance of surviving your emergency situation. The definition of what is a critical allocation and what the emergency situation is left intentionally somewhat vague, so as to offer more flexibility. For our use, certain networking allocations are critical and our emergency situation is a 2 minute window of potential exreme memory pressure. For others it could be something completely different, but the expectation is that the emergency situation would be of a finite time, since the pool is a fixed size.

What's your plan for handling the no-room-to-receive-ACKs problem?

Without addressing this, this is a non-starter for most of the network OOM problems I care about.

Signature · 7 lines
Mathematics is the supreme nostalgia of our time.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#23 lore raw
In reply to Matthew Dobson

Hi!

Quoted 5 lines
Quoted 4 lines

I don't see how this can ever work.

How can _userspace_ know about what allocations are critical to the kernel?!

Quoted 7 lines

Well, it isn't userspace that is determining *which* allocations are critical to the kernel. That is statically determined at compile time by using the flag __GFP_CRITICAL on specific *kernel* allocations. Sridhar, cc'd on this mail, has a set of patches that sprinkle the __GFP_CRITICAL flag throughout the networking code to take advantage of this pool. Userspace is in charge of determining *when* we're in an emergency situation, and should thus use the critical pool, but not *which*

It still is not too reliable. If you userspace tool is swapped out (etc), it may not get chance to wake up.

Quoted 10 lines
Quoted 4 lines

And as you noticed, it does not work for your original usage case, because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Well, I never suggested it didn't work for my original usage case. The discussion we had is that it would be incredibly difficult to 100% iron-clad guarantee that the pool would NEVER run out of pages. But we can size the pool, especially given a decent workload approximation, so as to make failure far less likely.

Perhaps you should add file in Documentation/ explaining it is not reliable?

Quoted 11 lines
Quoted 5 lines

If you want few emergency pages for some strange hack you are doing (swapping over network?), just put swap into ramdisk and swapon() it when you are in emergency, or use memory hotplug and plug few more gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Well, that's basically the point of posting these patches as an RFC. I'm not quite so delusional as to think they're going to get picked up right now. I was, however, hoping for feedback to figure out how to design infrastructure that *can* be used right, as well as trying to find other potential users of such a feature.

Well, we don't usually take infrastructure that has no in-kernel users, and example user would indeed be nice.
Pavel

Signature · 7 lines
Thanks, Sharp!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#20 lore raw
In reply to Matthew Dobson

Hi!

Quoted 6 lines
Quoted 5 lines

The whole extra critical level seems dubious in itself. In 2.0/2.2 days there were a set of patches that just dropped incoming memory on sockets when the memory was tight unless they were marked as critical (ie NFS swap). It worked rather well. The rest of the changes beyond that seem excessive.

Quoted 7 lines

Actually, Sridhar's code (mentioned earlier in this thread) *does* drop incoming packets that are not 'critical', but unfortunately you need to completely copy the packet into kernel memory before you can do any processing on it to determine whether or not it's 'critical', and thus accept or reject it. If network traffic is coming in at a good clip and the system is already under memory pressure, it's going to be difficult to receive all these packets, which was the inspiration for this patchset.

You should be able to do all this with single, MTU-sized buffer.

Receive packet into buffer. If it is nice, pass it up, otherwise drop it. Yes, it may drop some "important" packets, but that's okay, packet loss is expected on networks.
Pavel

Signature · 7 lines
Thanks, Sharp!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#24 lore raw
In reply to Pavel Machek
Quoted 12 lines · Pavel Machek
Quoted 10 lines
Quoted 4 lines

And as you noticed, it does not work for your original usage case, because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Well, I never suggested it didn't work for my original usage case. The discussion we had is that it would be incredibly difficult to 100% iron-clad guarantee that the pool would NEVER run out of pages. But we can size the pool, especially given a decent workload approximation, so as to make failure far less likely.

Quoted 2 lines

Perhaps you should add file in Documentation/ explaining it is not reliable?

That's a good suggestion. I will rework the patch's additions to Documentation/sysctl/vm.txt to be more clear about exactly what we're providing.

Quoted 13 lines
Quoted 11 lines
Quoted 5 lines

If you want few emergency pages for some strange hack you are doing (swapping over network?), just put swap into ramdisk and swapon() it when you are in emergency, or use memory hotplug and plug few more gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Well, that's basically the point of posting these patches as an RFC. I'm not quite so delusional as to think they're going to get picked up right now. I was, however, hoping for feedback to figure out how to design infrastructure that *can* be used right, as well as trying to find other potential users of such a feature.

Quoted 3 lines

Well, we don't usually take infrastructure that has no in-kernel users, and example user would indeed be nice.
Pavel

Understood. I certainly wouldn't expect otherwise. I'll see if I can get Sridhar to post his networking changes that take advantage of this.

Thanks!

-Matt

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#25 lore raw
Quoted 42 lines · Matthew Dobson
Quoted 13 lines · Pavel Machek
Quoted 6 lines
Quoted 6 lines

And as you noticed, it does not work for your original usage case, because reserved memory pool would have to be "sum of all network interface bandwidths * ammount of time expected to survive without network" which is way too much.

Quoted 7 lines

Well, I never suggested it didn't work for my original usage case. The discussion we had is that it would be incredibly difficult to 100% iron-clad guarantee that the pool would NEVER run out of pages. But we can size the pool, especially given a decent workload approximation, so as to make failure far less likely.

Quoted 4 lines

Perhaps you should add file in Documentation/ explaining it is not reliable?

That's a good suggestion. I will rework the patch's additions to Documentation/sysctl/vm.txt to be more clear about exactly what we're providing.

Quoted 14 lines
Quoted 14 lines
Quoted 7 lines

If you want few emergency pages for some strange hack you are doing (swapping over network?), just put swap into ramdisk and swapon() it when you are in emergency, or use memory hotplug and plug few more gigabytes into your machine. But don't go introducing infrastructure that _can't_ be used right.

Well, that's basically the point of posting these patches as an RFC. I'm not quite so delusional as to think they're going to get picked up right now. I was, however, hoping for feedback to figure out how to design infrastructure that *can* be used right, as well as trying to find other potential users of such a feature.

Quoted 10 lines
Quoted 5 lines

Well, we don't usually take infrastructure that has no in-kernel users, and example user would indeed be nice.
Pavel

Understood. I certainly wouldn't expect otherwise. I'll see if I can get Sridhar to post his networking changes that take advantage of this.

I have posted these patches yesterday on lkml and netdev and here is a link to the thread.
http://thread.gmane.org/gmane.linux.kernel/357835

Thanks
Sridhar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>