Merge branch 'master' of ssh://brick.kernel.dk/data/git/fio
diff --git a/HOWTO b/HOWTO
index bc23052..eb8f453 100644
--- a/HOWTO
+++ b/HOWTO
@@ -158,6 +158,9 @@
 
 $ fio --name=random-writers --ioengine=libaio --iodepth=4 --rw=randwrite --bs=32k --direct=0 --size=64m --numjobs=4
 
+4.1 Environment variables
+-------------------------
+
 fio also supports environment variable expansion in job files. Any
 substring of the form "${VARNAME}" as part of an option value (in other
 words, on the right of the `='), will be expanded to the value of the
@@ -188,6 +191,20 @@
 fio ships with a few example job files, you can also look there for
 inspiration.
 
+4.2 Reserved keywords
+---------------------
+
+Additionally, fio has a set of reserved keywords that will be replaced
+internally with the appropriate value. Those keywords are:
+
+$pagesize	The architecture page size of the running system
+$mb_memory	Megabytes of total memory in the system
+$ncpus		Number of online available CPUs
+
+These can be used on the command line or in the job file, and will be
+automatically substituted with the current system values when the job
+is run.
+
 
 5.0 Detailed list of parameters
 -------------------------------
diff --git a/fio.c b/fio.c
index 7ad1b57..dfd15e0 100644
--- a/fio.c
+++ b/fio.c
@@ -1618,12 +1618,6 @@
 	if (!getenv("LC_NUMERIC"))
 		setlocale(LC_NUMERIC, "en_US");
 
-	if (parse_options(argc, argv))
-		return 1;
-
-	if (!thread_number)
-		return 0;
-
 	ps = sysconf(_SC_PAGESIZE);
 	if (ps < 0) {
 		log_err("Failed to get page size\n");
@@ -1633,6 +1627,14 @@
 	page_size = ps;
 	page_mask = ps - 1;
 
+	fio_keywords_init();
+
+	if (parse_options(argc, argv))
+		return 1;
+
+	if (!thread_number)
+		return 0;
+
 	if (write_bw_log) {
 		setup_log(&agg_io_log[DDIR_READ]);
 		setup_log(&agg_io_log[DDIR_WRITE]);
diff --git a/fio.h b/fio.h
index 56d3101..049692d 100644
--- a/fio.h
+++ b/fio.h
@@ -511,6 +511,7 @@
  */
 extern int __must_check parse_options(int, char **);
 extern int fio_options_parse(struct thread_data *, char **, int);
+extern void fio_keywords_init(void);
 extern int fio_cmd_option_parse(struct thread_data *, const char *, char *);
 extern void fio_fill_default_options(struct thread_data *);
 extern int fio_show_option_help(const char *);
diff --git a/options.c b/options.c
index ef20c24..ebf5d2e 100644
--- a/options.c
+++ b/options.c
@@ -1702,14 +1702,102 @@
 	}
 }
 
+struct fio_keyword {
+	const char *word;
+	const char *desc;
+	char *replace;
+};
+
+static struct fio_keyword fio_keywords[] = {
+	{
+		.word	= "$pagesize",
+		.desc	= "Page size in the system",
+	},
+	{
+		.word	= "$mb_memory",
+		.desc	= "Megabytes of memory online",
+	},
+	{
+		.word	= "$ncpus",
+		.desc	= "Number of CPUs online in the system",
+	},
+	{
+		.word	= NULL,
+	},
+};
+
+void fio_keywords_init(void)
+{
+	unsigned long mb_memory;
+	char buf[128];
+	long l;
+
+	sprintf(buf, "%lu", page_size);
+	fio_keywords[0].replace = strdup(buf);
+
+	l = sysconf(_SC_PHYS_PAGES);
+	mb_memory = l * (page_size / 1024UL);
+	sprintf(buf, "%lu", mb_memory);
+	fio_keywords[1].replace = strdup(buf);
+
+	l = sysconf(_SC_NPROCESSORS_ONLN);
+	sprintf(buf, "%lu", l);
+	fio_keywords[2].replace = strdup(buf);
+}
+
+/*
+ * Look for reserved variable names and replace them with real values
+ */
+static char *fio_keyword_replace(char *opt)
+{
+	char *s;
+	int i;
+
+	for (i = 0; fio_keywords[i].word != NULL; i++) {
+		struct fio_keyword *kw = &fio_keywords[i];
+
+		while ((s = strstr(opt, kw->word)) != NULL) {
+			char *new = malloc(strlen(opt) + 1);
+			char *o_org = opt;
+			int olen = s - opt;
+			int len;
+
+			/*
+			 * Copy part of the string before the keyword and
+			 * sprintf() the replacement after it.
+			 */
+			memcpy(new, opt, olen);
+			len = sprintf(new + olen, "%s", kw->replace);
+
+			/*
+			 * If there's more in the original string, copy that
+			 * in too
+			 */
+			opt += strlen(kw->word) + olen;
+			if (strlen(opt))
+				memcpy(new + olen + len, opt, opt - o_org - 1);
+
+			/*
+			 * replace opt and free the old opt
+			 */
+			opt = new;
+			free(o_org);
+		}
+	}
+
+	return opt;
+}
+
 int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
 {
 	int i, ret;
 
 	sort_options(opts, options, num_opts);
 
-	for (ret = 0, i = 0; i < num_opts; i++)
+	for (ret = 0, i = 0; i < num_opts; i++) {
+		opts[i] = fio_keyword_replace(opts[i]);
 		ret |= parse_option(opts[i], options, td);
+	}
 
 	return ret;
 }