23b1b8
From 55f7ba830d40d438f0b0663a505e0c227fc68b6b Mon Sep 17 00:00:00 2001
23b1b8
From: Phillip Lougher <phillip@squashfs.org.uk>
23b1b8
Date: Tue, 10 Jun 2014 21:51:52 +0100
23b1b8
Subject: mksquashfs: fix phys mem calculation for 32-bit processes on
23b1b8
 PAE/64-bit kernels
23b1b8
23b1b8
When adding the code to base default memory usage on physical memory
23b1b8
(by default use 25% of physical memory), I made an oversight.  I assumed
23b1b8
the process would be able to address 25% of physical memory.
23b1b8
23b1b8
However, for 32-bit processes running on a PAE kernel or 64-bit kernel,
23b1b8
25% of physical memory can easily exceed the addressible memory for a
23b1b8
32-bit process, e.g. if a machine has 24 GB of physical memory, the
23b1b8
code would asume the process could easily use 6 GB.
23b1b8
23b1b8
A 32-bit process by definition can only address 4 GB (32-bit pointers).
23b1b8
But, due to the typical kernel/user-space split (1GB/3GB, or 2GB/2GB)
23b1b8
on PAE kernels, a 32-bit process may only be able to address 2 GB.
23b1b8
23b1b8
So, if Mksquashfs is a 32-bit application running on a PAE/64-bit kernel,
23b1b8
the code assumes it can address much more memory than it really can, which
23b1b8
means it runs out of memory.
23b1b8
23b1b8
The fix is to impose a maximum default limit on 32-bit kernels, or
23b1b8
otherwise to never use a value more than 25% of the address space.  If
23b1b8
we assume the maximum address space is 2 GB, then the maximum becomes
23b1b8
512 MB.  But, given most kernels used the 1GB/3GB split, that may be
23b1b8
unduely conservative, and 25% of 3 GB (756 MB) may be better.  This
23b1b8
patch compromises on 640 MB, which is mid-way between the 512 MB and 756 MB
23b1b8
values.  It is also the fixed default value previously used by Mksquashfs.
23b1b8
23b1b8
This patch also alters the code which imposes a maximum size.  Previously
23b1b8
it was believed limiting to the physical memory size was adequate.  But
23b1b8
obviously this needs to be updated to take into account a 32-bit process
23b1b8
may only be able to address 2 GB.  In the process I've also taken the
23b1b8
opportunity to limit all requests to no more than 75% of physical memory.
23b1b8
23b1b8
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
23b1b8
23b1b8
diff --git a/squashfs-tools/mksquashfs.c b/squashfs-tools/mksquashfs.c
23b1b8
index 86f82bb..5370ecf 100644
23b1b8
--- a/squashfs-tools/mksquashfs.c
23b1b8
+++ b/squashfs-tools/mksquashfs.c
23b1b8
@@ -304,7 +304,7 @@ void restorefs();
23b1b8
 struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth);
23b1b8
 void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad);
23b1b8
 unsigned short get_checksum_mem(char *buff, int bytes);
23b1b8
-int get_physical_memory();
23b1b8
+void check_usable_phys_mem(int total_mem);
23b1b8
 
23b1b8
 
23b1b8
 void prep_exit()
23b1b8
@@ -4053,11 +4053,7 @@ void initialise_threads(int readq, int fragq, int bwriteq, int fwriteq,
23b1b8
 		BAD_ERROR("Queue sizes rediculously too large\n");
23b1b8
 	total_mem += fwriteq;
23b1b8
 
23b1b8
-	if(total_mem > get_physical_memory()) {
23b1b8
-		ERROR("Total queue sizes larger than physical memory.\n");
23b1b8
-		ERROR("Mksquashfs will exhaust physical memory and thrash.\n");
23b1b8
-		BAD_ERROR("Queues too large\n");
23b1b8
-	}
23b1b8
+	check_usable_phys_mem(total_mem);
23b1b8
 
23b1b8
 	/*
23b1b8
 	 * convert from queue size in Mbytes to queue size in
23b1b8
@@ -4879,6 +4875,72 @@ int get_physical_memory()
23b1b8
 }
23b1b8
 
23b1b8
 
23b1b8
+void check_usable_phys_mem(int total_mem)
23b1b8
+{
23b1b8
+	/*
23b1b8
+	 * We want to allow users to use as much of their physical
23b1b8
+	 * memory as they wish.  However, for practical reasons there are
23b1b8
+	 * limits which need to be imposed, to protect users from themselves
23b1b8
+	 * and to prevent people from using Mksquashfs as a DOS attack by using
23b1b8
+	 * all physical memory.   Mksquashfs uses memory to cache data from disk
23b1b8
+	 * to optimise performance.  It is pointless to ask it to use more
23b1b8
+	 * than 75% of physical memory, as this causes thrashing and it is thus
23b1b8
+	 * self-defeating.
23b1b8
+	 */
23b1b8
+	int mem = get_physical_memory();
23b1b8
+
23b1b8
+	mem = (mem >> 1) + (mem >> 2); /* 75% */
23b1b8
+						
23b1b8
+	if(total_mem > mem) {
23b1b8
+		ERROR("Total memory requested is more than 75%% of physical "
23b1b8
+						"memory.\n");
23b1b8
+		ERROR("Mksquashfs uses memory to cache data from disk to "
23b1b8
+						"optimise performance.\n");
23b1b8
+		ERROR("It is pointless to ask it to use more than this amount "
23b1b8
+						"of memory, as this\n");
23b1b8
+		ERROR("causes thrashing and it is thus self-defeating.\n");
23b1b8
+		BAD_ERROR("Requested memory size too large\n");
23b1b8
+	}
23b1b8
+
23b1b8
+	if(sizeof(void *) == 4 && total_mem > 2048) {
23b1b8
+		/*
23b1b8
+		 * If we're running on a kernel with PAE or on a 64-bit kernel,
23b1b8
+		 * then the 75% physical memory limit can still easily exceed
23b1b8
+		 * the addressable memory by this process.
23b1b8
+		 *
23b1b8
+		 * Due to the typical kernel/user-space split (1GB/3GB, or
23b1b8
+		 * 2GB/2GB), we have to conservatively assume the 32-bit
23b1b8
+		 * processes can only address 2-3GB.  So refuse if the user
23b1b8
+		 * tries to allocate more than 2GB.
23b1b8
+		 */
23b1b8
+		ERROR("Total memory requested may exceed maximum "
23b1b8
+				"addressable memory by this process\n");
23b1b8
+		BAD_ERROR("Requested memory size too large\n");
23b1b8
+	}
23b1b8
+}
23b1b8
+
23b1b8
+
23b1b8
+int get_default_phys_mem()
23b1b8
+{
23b1b8
+	int mem = get_physical_memory() / SQUASHFS_TAKE;
23b1b8
+
23b1b8
+	if(sizeof(void *) == 4 && mem > 640) {
23b1b8
+		/*
23b1b8
+		 * If we're running on a kernel with PAE or on a 64-bit kernel,
23b1b8
+		 * the default memory usage can exceed the addressable
23b1b8
+		 * memory by this process.
23b1b8
+		 * Due to the typical kernel/user-space split (1GB/3GB, or
23b1b8
+		 * 2GB/2GB), we have to conservatively assume the 32-bit
23b1b8
+		 * processes can only address 2-3GB.  So limit the  default
23b1b8
+		 * usage to 640M, which gives room for other data.
23b1b8
+		 */
23b1b8
+		mem = 640;
23b1b8
+	}
23b1b8
+
23b1b8
+	return mem;
23b1b8
+}
23b1b8
+
23b1b8
+
23b1b8
 void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq,
23b1b8
 							int *fwriteq)
23b1b8
 {
23b1b8
@@ -4890,7 +4952,7 @@ void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq,
23b1b8
 
23b1b8
 
23b1b8
 #define VERSION() \
23b1b8
-	printf("mksquashfs version 4.3 (2014/05/12)\n");\
23b1b8
+	printf("mksquashfs version 4.3-git (2014/06/09)\n");\
23b1b8
 	printf("copyright (C) 2014 Phillip Lougher "\
23b1b8
 		"<phillip@squashfs.org.uk>\n\n"); \
23b1b8
 	printf("This program is free software; you can redistribute it and/or"\
23b1b8
@@ -4918,7 +4980,7 @@ int main(int argc, char *argv[])
23b1b8
 	int fragq;
23b1b8
 	int bwriteq;
23b1b8
 	int fwriteq;
23b1b8
-	int total_mem = get_physical_memory() / SQUASHFS_TAKE;
23b1b8
+	int total_mem = get_default_phys_mem();
23b1b8
 	int progress = TRUE;
23b1b8
 	int force_progress = FALSE;
23b1b8
 	struct file_buffer **fragment = NULL;
23b1b8
-- 
23b1b8
cgit v0.10.1
23b1b8