Index: sreadahead.c
===================================================================
--- sreadahead.c	(revision 38)
+++ sreadahead.c	(working copy)
@@ -8,6 +8,20 @@
  * as published by the Free Software Foundation; version 2
  * of the License.
  */
+
+/*
+ * TODO: 
+ *	set CPU priority at maximum [!]
+ *	do the mincore earlier ?
+ *	print out files with no touched pages / special-case them ?
+ *	delay a loop and run the mincore later ? ...
+ *
+ *	merge contiguous future stuff into the 1st 256 chunk ?
+ *	so we don't suffer ? ...
+ *
+ *      add breakdown of I/O by paths ... (?)
+ */
+
 #define _GNU_SOURCE
 
 #include <stdio.h>
@@ -25,9 +39,10 @@
 #include <sys/types.h>
 #include <sys/syscall.h>
 #include <sys/mount.h>
+#include <sys/ioctl.h>
 #include <sys/signal.h>
-#include <fcntl.h>
 #include <errno.h>
+#include <linux/fs.h>
 
 #include <getopt.h>
 
@@ -47,9 +62,14 @@
 #  warning "Architecture does not support ioprio modification"
 #endif
 #define IOPRIO_WHO_PROCESS 1
+#define IOPRIO_WHO_PGRP    2
+#define IOPRIO_CLASS_RT 1
+#define IOPRIO_CLASS_BE 2
 #define IOPRIO_CLASS_IDLE 3
 #define IOPRIO_CLASS_SHIFT 13
 #define IOPRIO_IDLE_LOWEST (7 | (IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT))
+#define IOPRIO_BE_HIGHEST  (0 | (IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT))
+#define IOPRIO_RT_HIGHEST  (0 | (IOPRIO_CLASS_RT << IOPRIO_CLASS_SHIFT))
 
 #define PACK_PATH	"/var/lib/sreadahead"
 #define DEBUGFS_MNT	"/var/lib/sreadahead/debugfs"
@@ -58,9 +78,12 @@
 #define MAXR 40000	/* trace file can be long */
 #define MAXFL 128
 #define MAXRECS 6	/* reduce nr of fragments to this amount */
+#define MAXTHREADS 16   /* max. number of read threads we can use */
 
-#define DEFAULT_MAX_TIME 15 /* should be enough for every OS to boot */
+#define DEFAULT_MAX_TIME 20 /* should be enough for every OS to boot */
 
+#define CHUNK_SIZE 256 /* deeply mystical I/O grouping chunk size */
+
 /*
  * By default, the kernel reads ahead for 128kb. This throws off our
  * measurements since we don't need the extra 128kb for each file.
@@ -99,6 +122,7 @@
 	struct ra_struct	*next;
 	struct ra_struct	*prev;
 	int			number;
+	unsigned long		block_order_hint;
 };
 
 static struct ra_struct *ra[MAXR];
@@ -112,15 +136,22 @@
 static unsigned int total_files = 0;
 static unsigned int cursor = 0;
 
+static int dump = 0;
 static int debug = 0;
+static int is_ssd = 0;
 
+static void set_ioprio (int prio)
+{
+#ifdef HAVE_IO_PRIO
+	if (syscall(__NR_ioprio_set, IOPRIO_WHO_PGRP, 0, prio) == -1)
+		perror("Can not set IO priority to idle class");
+#endif
+}
 
-static void readahead_set_len(int size)
+static int sysfs_unmount = 0;
+static void enter_sysfs (void)
 {
 	int unmount;
-	int i = 0;
-	char ractl[100];
-	/* changes readahead size to "size" for local block devices */
 
 	unmount = chdir("/sys/block");
 	if (unmount != 0) {
@@ -129,9 +160,75 @@
 			/* non-fatal */
 			return;
 		}
+		sysfs_unmount = 1;
 		chdir("/sys/block");
+	} else
+		sysfs_unmount = 0;
+}
+
+static void exit_sysfs (void)
+{
+	chdir("/");
+	if (sysfs_unmount != 0)
+		umount("/sys");
+}
+
+static int debugfs_unmount = 0;
+static void enter_debugfs (void)
+{
+	/*
+	 * by now the init process should have mounted debugfs on a logical
+	 * location like /sys/kernel/debug, but if not then we temporarily
+	 * re-mount it ourselves
+	 */
+	debugfs_unmount = chdir("/sys/kernel/debug/tracing");
+	if (debugfs_unmount != 0) {
+		int ret = mount("debugfs", DEBUGFS_MNT, "debugfs", 0, NULL);
+		if (ret != 0) {
+			perror("Unable to mount debugfs\n");
+			exit(EXIT_FAILURE);
+		}
+		chdir(DEBUGFS_MNT);
+	} else {
+		chdir("..");
 	}
+}
 
+static void exit_debugfs (void)
+{
+	chdir("/");
+	if (debugfs_unmount != 0) {
+		umount(DEBUGFS_MNT);
+	}
+}
+
+static int is_sda_ssd (void)
+{
+	FILE *file;
+	int is_ssd = 0;
+
+	enter_sysfs();
+
+	file = fopen ("sda/queue/rotational", "r");
+	if (file) {
+		char buffer[64];
+		is_ssd = !atoi (fgets (buffer, 64, file));
+		fclose (file);
+	}
+
+	exit_sysfs();
+
+	return is_ssd;
+}
+
+static void readahead_set_len(int size)
+{
+	int i = 0;
+	char ractl[100];
+	/* changes readahead size to "size" for local block devices */
+
+	enter_sysfs();
+
 	sprintf(ractl, "sda/queue/read_ahead_kb");
 	while (i <= 3) {
 		/* check first 4 sata discs */
@@ -144,10 +241,16 @@
 		i++;
 	}
 
-	chdir("/");
+	exit_sysfs();
+}
 
-	if (unmount != 0)
-		umount("/sys");
+static void kmsg_print(const char *msg)
+{
+	int fd = open("/dev/kmsg", O_WRONLY);
+	if (fd > 0) {
+		write (fd, msg, strlen (msg));
+		close (fd);
+	}
 }
 
 static void readahead_one(int index)
@@ -156,6 +259,11 @@
 	int i;
 	char buf[128];
 
+	if (index == CHUNK_SIZE) {
+		kmsg_print ("sreadahead hdd - have read first chunk(s)\n");
+		set_ioprio (IOPRIO_IDLE_LOWEST);
+	}
+
 	fd = open(rd[index].filename, O_RDONLY|O_NOATIME);
 	if (fd < 0)
 		fd = open(rd[index].filename, O_RDONLY);
@@ -187,6 +295,89 @@
 	return NULL;
 }
 
+static int dump_files(void)
+{
+	unsigned int i;
+	FILE *out = stdout;
+
+	for (i = 0; i < total_files; i++) {
+		int page_size = 4096;
+		struct stat statbuf;
+		size_t num_pages;
+		size_t block_count, j;
+		off_t block_bytes;
+		char *buf = NULL;
+		char *ptr;
+
+		if (!(i % CHUNK_SIZE))
+			fprintf (out, "%d chunk boundary.\n", i / CHUNK_SIZE);
+
+		if (stat (rd[i].filename, &statbuf) < 0) {
+			fprintf (out, "%s: %s\n", rd[i].filename, strerror (errno));
+			continue;
+		}
+
+		fprintf (out, "%s\n", rd[i].filename);
+
+		num_pages = (statbuf.st_size
+			     ? (statbuf.st_size - 1) / page_size + 1
+			     : 0);
+
+		buf = malloc (num_pages + 1);
+		memset (buf, '.', num_pages);
+		buf[num_pages] = '\0';
+
+		block_count = 0;
+		block_bytes = 0;
+
+		for (j = 0; j < MAXRECS; j++) {
+		  size_t blk;
+		  size_t k;
+
+			if (!rd[i].data[j].len)
+				continue;
+			blk = rd[i].data[j].offset / page_size;
+
+			if (blk < num_pages)
+				buf[blk] = '@';
+
+			for (k = blk + 1;
+			     ((k < (blk + (rd[i].data[j].len + page_size - 1) / page_size))
+			      && (k < num_pages));
+			     k++)
+				buf[k] = '#';
+
+			block_count++;
+			block_bytes += rd[i].data[j].len;
+		}
+
+		fprintf (out, "%s (%zu kB), %zu blocks (%zu kB)\n",
+			 rd[i].filename, (size_t)(statbuf.st_size + 1023) / 1024,
+			 block_count, (size_t)(block_bytes + 1023) / 1024);
+
+		ptr = buf;
+		while (strlen (ptr) > 74) {
+			fprintf (out, "  [%.74s]\n", ptr);
+			ptr += 74;
+		}
+
+		if (strlen (ptr))
+			fprintf (out, "  [%s]\n", ptr);
+
+		for (j = 0; j < MAXRECS; j++) {
+			if (!rd[i].data[j].len)
+				continue;
+
+			fprintf (out, "\t%zu, %zu bytes\n",
+				 (size_t)rd[i].data[j].offset,
+				 (size_t)rd[i].data[j].len);
+		}
+	}
+	return EXIT_SUCCESS;
+}
+
+/* sort to help remove duplicates, we retain the original
+   order in the next/prev linked list */
 static void sort_ra_by_name(void)
 {
 	int delta = 1;
@@ -343,6 +534,11 @@
 		rcount++;
 	}
 
+	if (there) {
+		r->block_order_hint = 0; /* first block */
+		ioctl (fd, FIBMAP, &r->block_order_hint);
+	}
+
 	free(mincorebuf);
 	munmap(mmapptr, statbuf.st_size);
 	fclose(file);
@@ -350,8 +546,12 @@
 	rcount = reduce_blocks(record, rcount, MAXRECS);
 	if (rcount > 0) {
 		/* some empty files slip through */
-		if (record[0].len == 0)
+		if (record[0].len == 0) {
+			if (debug)
+				printf ("removing head '%s' size %ld\n",
+					r->filename, statbuf.st_size);
 			return 0;
+		}
 
 		if (debug) {
 			int tlen = 0;
@@ -362,15 +562,26 @@
 				fcount++;
 			}
 			rdsize += (tlen <= 0 ? 1024 : tlen);
-			printf("%s: %d fragment(s), %dkb, %3.1f%%\n",
+			printf("%s: %d fragment(s), %dkb, %3.1f%% - block %ld\n",
 			       r->filename, rcount,
 			       (tlen <= 1024 ? 1024 : tlen) / 1024,
-			       100.0 * there / (there + notthere));
+			       100.0 * there / (there + notthere),
+			       r->block_order_hint);
 		}
 
 		memcpy(r->data, record, sizeof(r->data));
 		return 1;
 	}
+	/* allow small files - dropped by the page cache through anyway */
+	else if (statbuf.st_size < 4096*2) {
+		r->data[0].offset = 0;
+		r->data[0].len = (uint32_t) statbuf.st_size;
+		return 1;
+	}
+	if (debug)
+		printf ("removing tail '%s' size %ld\n",
+			r->filename, statbuf.st_size);
+	return 0;
 }
 
 static void get_ra_blocks(void)
@@ -389,6 +600,61 @@
 	}
 }
 
+static void write_ra (FILE *file, struct ra_struct *r)
+{
+	if (debug)
+		printf ("write_ra '%s' (0x%lx)\n", r->filename, r->block_order_hint);
+	fwrite(r->filename, MAXFL, 1, file);
+	fwrite(r->data, sizeof(r->data), 1, file);
+	rdcount++;
+}
+
+/* split the list of files into chunks - runs of 256 files
+   or so. Inside this chunk, sort by block hint - hopefully
+   this substantially improves read linearity on non-SSDs */
+static void write_sorted_in_chunks_by_block(FILE *file, struct ra_struct *list)
+{
+	while (list) {
+		int i, max = 0;
+		int delta = 1;
+		struct ra_struct *sort_array[CHUNK_SIZE];
+
+		/* copy a chunk across */
+		for (; list && max < CHUNK_SIZE; list = list->next)
+			sort_array[max++] = list;
+
+		/* sort by first block */
+		while (delta > 0) {
+			delta = 0;
+			for (i = 0; i < max - 1; i++) {
+				if (sort_array[i]->block_order_hint > sort_array[i+1]->block_order_hint) {
+					struct ra_struct *tmp;
+					tmp = sort_array[i];
+					sort_array[i] = sort_array[i+1];
+					sort_array[i+1] = tmp;
+					delta++;
+				}
+			}
+		}
+
+		/* write out */
+		for (i = 0; i < max - 1; i++)
+			write_ra (file, sort_array[i]);
+	}
+}
+
+static void trace_fprintf (const char *fname, const char *value)
+{
+	FILE *file = fopen (fname, "w");
+	if (!file) {
+		fprintf (stderr, "Unable to open %s: %s\n",
+			 fname, strerror (errno));
+		exit(EXIT_FAILURE);
+	}
+	fputs (value, file);
+	fclose(file);
+}
+
 static void trace_start(void)
 {
 	int ret;
@@ -408,30 +674,11 @@
 
 	chdir(DEBUGFS_MNT);
 
-	file = fopen("tracing/current_tracer", "w");
-	if (!file) {
-		perror("Unable to select tracer\n");
-		exit(EXIT_FAILURE);
-	}
-	fprintf(file, "open");
-	fclose(file);
+	trace_fprintf ("tracing/events/fs-open/uselib/enable", "1");
+	trace_fprintf ("tracing/events/fs-open/open_exec/enable", "1");
+	trace_fprintf ("tracing/events/fs-open/do_sys_open/enable", "1");
+	trace_fprintf ("tracing/tracing_enabled", "1");
 
-	file = fopen("tracing/current_tracer", "r");
-	fgets(buf, 4096, file);
-	fclose(file);
-	if (strcmp(buf, "open\n") != 0) {
-		perror("Unable to select open tracer\n");
-		exit(EXIT_FAILURE);
-	}
-
-	file = fopen("tracing/tracing_enabled", "w");
-	if (!file) {
-		perror("Unable to enable tracing\n");
-		exit(EXIT_FAILURE);
-	}
-	fprintf(file, "1");
-	fclose(file);
-
 	file = fopen("tracing/tracing_enabled", "r");
 	fgets(buf, 4096, file);
 	fclose(file);
@@ -448,54 +695,43 @@
 	readahead_set_len(RA_SMALL);
 }
 
-static void trace_stop(int signal)
+static int trace_terminate = 0;
+static void trace_signal(int signal)
 {
-	int unmount;
-	int ret;
+	trace_terminate = 1;
+}
+
+static int dumb_hash_contains (const char *filename)
+{
+	static const char *hash_table[2039] = { 0, };
+	const signed char *p = filename;
+	uint32_t h = *p;
+	for (p += 1; *p != '\0'; p++)
+		h = (h << 5) - h + *p;
+	h %= 2039;
+	if (hash_table[h])
+		return !strcmp (hash_table[h], filename);
+	hash_table[h] = filename;
+	return 0;
+}
+
+static void read_trace_pipe(void)
+{
+	int fd;
+	int orig_racount = racount;
 	char buf[4096];
 	char filename[4096];
 	FILE *file;
-	struct ra_struct *r;
-	struct tms start_time;
-	struct tms stop_time;
 
-	if (debug)
-		times(&start_time);
-
-	nice(20);
-
 	/* return readahead size to normal */
 	readahead_set_len(RA_NORMAL);
+	
+	enter_debugfs();
 
-	/*
-	 * by now the init process should have mounted debugfs on a logical
-	 * location like /sys/kernel/debug, but if not then we temporarily
-	 * re-mount it ourselves
-	 */
-	unmount = chdir("/sys/kernel/debug/tracing");
-	if (unmount != 0) {
-		ret = mount("debugfs", DEBUGFS_MNT, "debugfs", 0, NULL);
-		if (ret != 0) {
-			perror("Unable to mount debugfs\n");
-			exit(EXIT_FAILURE);
-		}
-		chdir(DEBUGFS_MNT);
-	} else {
-		chdir("..");
-	}
-
-	/* stop tracing */
-	file = fopen("tracing/tracing_enabled", "w");
+	fd = open ("tracing/trace_pipe", O_RDONLY);
+	fcntl (fd, F_SETFL, O_NONBLOCK);
+	file = fdopen(fd, "r");
 	if (!file) {
-		perror("Unable to disable tracing\n");
-		/* non-fatal */
-	} else {
-		fprintf(file, "0");
-		fclose(file);
-	}
-
-	file = fopen("tracing/trace", "r");
-	if (!file) {
 		perror("Unable to open trace file\n");
 		exit(EXIT_FAILURE);
 	}
@@ -523,12 +759,20 @@
 			continue;
 		if (strncmp(filename, "/proc/", 6) == 0)
 			continue;
+		if (strncmp(filename, "/tmp/bootchart", 14) == 0)
+			continue;
 
 		if (racount >= MAXR) {
 			perror("Max records exceeded!");
 			break;
 		}
 
+		/* magic file; open me to abort sreadahead */
+		if (strstr (filename, "sreadahead.complete.token")) {
+			trace_terminate = 1;
+			break;
+		}
+
 		if (strlen(filename) <= MAXFL) {
 			struct ra_struct *tmp;
 			tmp = malloc(sizeof(struct ra_struct));
@@ -542,6 +786,14 @@
 			ra[racount] = tmp;
 
 			strcpy(ra[racount]->filename, filename);
+
+			/* save hammering the lamers bubble-sort with duplicates */
+			if (dumb_hash_contains (ra[racount]->filename)) {
+				free (ra[racount]);
+				ra[racount] = NULL;
+				continue;
+			}
+
 			if (racount > 0) {
 				ra[racount]->prev = ra[racount - 1];
 				ra[racount - 1]->next = ra[racount];
@@ -553,15 +805,35 @@
 	fclose(file);
 
 	if (debug)
+		printf ("read %d records\n", racount - orig_racount);
+
+	exit_debugfs();
+}
+
+static void trace_stop(void)
+{
+	FILE *file;
+	struct ra_struct *r;
+	struct tms start_time;
+	struct tms stop_time;
+
+	if (debug) {
+		times(&start_time);
 		printf("Trace contained %d records\n", racount);
+	}
 
+	enter_debugfs();
+
+	/* stop tracing */
+	trace_fprintf ("tracing/tracing_enabled", "0");
+	trace_fprintf ("tracing/events/fs-open/do_sys_open/enable", "0");
+	trace_fprintf ("tracing/events/fs-open/open_exec/enable", "0");
+	trace_fprintf ("tracing/events/fs-open/uselib/enable", "0");
+
+	exit_debugfs();
+
 	first_ra = ra[0];
 
-	chdir("/");
-	if (unmount != 0) {
-		umount(DEBUGFS_MNT);
-	}
-
 	/*
 	 * sort and filter duplicates, and get memory blocks
 	 */
@@ -578,13 +850,13 @@
 		exit(EXIT_FAILURE);
 	}
 
-	r = first_ra;
-	while (r) {
-		fwrite(r->filename, MAXFL, 1, file);
-		fwrite(r->data, sizeof(r->data), 1, file);
-		r = r->next;
-		rdcount++;
+	if (!is_ssd)
+		write_sorted_in_chunks_by_block (file, first_ra);
+	else {
+		for (r = first_ra; r; r = r->next)
+			write_ra (file, r);
 	}
+
 	fclose(file);
 	if (debug) {
 		times(&stop_time);
@@ -604,6 +876,7 @@
 	printf("  -t N, --time=N        Wait for N seconds before creating new\n");
 	printf("                        pack file (default %d)\n", DEFAULT_MAX_TIME);
 	printf("  -d,   --debug         Print debug output to stdout\n");
+	printf("  -x,   --dump          Dump the pack to stdout\n");
 	printf("  -h,   --help          Show this help message\n");
 	printf("  -v,   --version       Show version information and exit\n");
 	exit(EXIT_SUCCESS);
@@ -619,12 +892,15 @@
 int main(int argc, char **argv)
 {
 	FILE *file;
-	int pid = 0;
-	pthread_t one, two, three, four;
+	int i, max_threads;
+	pthread_t threads[MAXTHREADS];
 	int max_time = DEFAULT_MAX_TIME;
 
+	kmsg_print ("sreadahead starting\n");
+
 	while (1) {
 		static struct option opts[] = {
+			{ "dump", 0, NULL, 'x' },
 			{ "debug", 0, NULL, 'd' },
 			{ "help", 0, NULL, 'h' },
 			{ "version", 0, NULL, 'v' },
@@ -650,26 +926,40 @@
 		case 't':
 			max_time = atoi(optarg);
 			break;
+		case 'x':
+			dump = 1;
+			break;
 		default:
 			;
 		}
 	}
 
+	is_ssd = is_sda_ssd ();
+	if (!is_ssd)
+		max_time *= 2;
+
 	file = fopen(PACK_FILE, "r");
 	if (!file) {
 		/* enable tracing open calls before we fork! */
 		trace_start();
 	
 		if (!fork()) {
-			/* child */
-			signal(SIGUSR1, trace_stop);
-			sleep(max_time);
+			int i, max;
+			max = max_time * 2;
+
+			trace_terminate = 0;
+			signal(SIGUSR1, trace_signal);
+
+			for (i = 0; i < max && !trace_terminate; i++) {
+			    usleep (1000000 / 2);
+			    read_trace_pipe ();
+			}
 			/*
 			 * abort if we don't get a signal, so we can stop
 			 * the tracing and minimize the trace buffer size
 			 */
 			signal(SIGUSR1, NULL);
-			trace_stop(0);
+			trace_stop();
 		} else {
 			return EXIT_SUCCESS;
 		}
@@ -682,28 +972,33 @@
 		return 1;
 	}
 	fclose(file);
+	if (dump)
+		return dump_files ();
 
-#ifdef HAVE_IO_PRIO
-	if (syscall(__NR_ioprio_set, IOPRIO_WHO_PROCESS, pid,
-		    IOPRIO_IDLE_LOWEST) == -1)
-		perror("Can not set IO priority to idle class");
-#endif
+	if (is_ssd) {
+		set_ioprio (IOPRIO_IDLE_LOWEST);	
+		readahead_set_len(RA_SMALL);
+		max_threads = 4;
+	} else {
+		set_ioprio (IOPRIO_RT_HIGHEST); /* will lower later */
+		max_threads = 1;
+	}
 
-	readahead_set_len(RA_SMALL);
-
 	daemon(0,0);
 
-	pthread_create(&one, NULL, one_thread, NULL);
-	pthread_create(&two, NULL, one_thread, NULL);
-	pthread_create(&three, NULL, one_thread, NULL);
-	pthread_create(&four, NULL, one_thread, NULL);
+	/* it is important that we get our I/O requests in first,
+	 * and our mincore's done before the page-cache gets
+	 * trampled on during instrumenting */
+	nice(-10);
 
-	pthread_join(one, NULL);
-	pthread_join(two, NULL);
-	pthread_join(three, NULL);
-	pthread_join(four, NULL);
+	for (i = 0; i < max_threads; i++)
+		pthread_create(&threads[i], NULL, one_thread, NULL);
 
-	readahead_set_len(RA_NORMAL);
+	for (i = 0; i < max_threads; i++)
+		pthread_join(threads[i], NULL);
 
+	if (is_ssd)
+		readahead_set_len(RA_NORMAL);
+
 	return EXIT_SUCCESS;
 }
Index: 0001-kernel-trace-open.patch
===================================================================
--- 0001-kernel-trace-open.patch	(revision 38)
+++ 0001-kernel-trace-open.patch	(working copy)
@@ -1,251 +1,273 @@
-commit e9c4f51ad2b76ddbf90863c44085574197247ba3
-Author: Auke Kok <auke-jan.h.kok@intel.com>
-Date:   Tue Jan 27 11:26:00 2009 -0800
+From michael.meeks@novell.com Fri Dec  4 09:44:01 2009
+Subject: io tracing patch vs. 2.6.31 ...
+From: Michael Meeks <michael.meeks@novell.com>
+To: Greg Kroah-Hartman <GregKH@novell.com>
+Date: Fri, 04 Dec 2009 17:47:04 +0000
+Message-Id: <1259948824.6186.1668.camel@linux-h3ht.site>
 
-    trace: tracer for sys_open() - sreadahead
-    
-    This tracer monitors regular file open() syscalls. This is a fast
-    and low-overhead alternative to strace, and does not allow or
-    require to be attached to every process.
-    
-    Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com>
+This patch, inspired by Arjan, and Scott James Remnant's work adds
+the ability to easily add trace points that refer to files, and
+print full paths.
 
-diff --git a/fs/open.c b/fs/open.c
-index a3a78ce..bd9eb9d 100644
+It also adds three of these trace points to open(), exec() and uselib()
+
+Signed-off-by: Michael Meeks <michael.meeks@novell.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/exec.c                    |    6 +++
+ fs/open.c                    |    4 ++
+ include/linux/ftrace_event.h |    4 ++
+ include/trace/events/fs.h    |   66 +++++++++++++++++++++++++++++++++++++++++++
+ include/trace/ftrace.h       |   28 ++++++++++++++++++
+ kernel/trace/trace_events.c  |   34 ++++++++++++++++++++++
+ 6 files changed, 142 insertions(+)
+
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -56,6 +56,8 @@
+ #include <linux/fsnotify.h>
+ #include <linux/fs_struct.h>
+ 
++#include <trace/events/fs.h>
++
+ #include <asm/uaccess.h>
+ #include <asm/mmu_context.h>
+ #include <asm/tlb.h>
+@@ -130,6 +132,8 @@ SYSCALL_DEFINE1(uselib, const char __use
+ 
+ 	fsnotify_open(file->f_path.dentry);
+ 
++	trace_uselib(file);
++
+ 	error = -ENOEXEC;
+ 	if(file->f_op) {
+ 		struct linux_binfmt * fmt;
+@@ -665,6 +669,8 @@ struct file *open_exec(const char *name)
+ 
+ 	fsnotify_open(file->f_path.dentry);
+ 
++	trace_open_exec(file);
++
+ 	err = deny_write_access(file);
+ 	if (err)
+ 		goto exit;
 --- a/fs/open.c
 +++ b/fs/open.c
-@@ -30,6 +30,10 @@
- #include <linux/audit.h>
+@@ -31,6 +31,9 @@
  #include <linux/falloc.h>
+ #include <linux/fs_struct.h>
  
-+#include <trace/fs.h>
++#define CREATE_TRACE_POINTS
++#include <trace/events/fs.h>
 +
-+DEFINE_TRACE(do_sys_open);
-+
  int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
  {
  	int retval = -ENODEV;
-@@ -1039,6 +1043,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
+@@ -1040,6 +1043,7 @@ long do_sys_open(int dfd, const char __u
  			} else {
  				fsnotify_open(f->f_path.dentry);
  				fd_install(fd, f);
-+				trace_do_sys_open(f, flags, mode, fd);
++				trace_do_sys_open(f, flags, mode);
  			}
  		}
  		putname(tmp);
-diff --git a/include/trace/fs.h b/include/trace/fs.h
-new file mode 100644
-index 0000000..870eec2
+--- a/include/linux/ftrace_event.h
++++ b/include/linux/ftrace_event.h
+@@ -169,4 +169,8 @@ do {									\
+ 	if (ret)							\
+ 		return ret;
+ 
++/* file pointer helpers */
++extern int  ftrace_file_name_len (const struct file *f);
++extern void ftrace_assign_file   (char *dest, int dest_len, const struct file *f);
++
+ #endif /* _LINUX_FTRACE_EVENT_H */
 --- /dev/null
-+++ b/include/trace/fs.h
-@@ -0,0 +1,11 @@
-+#ifndef _TRACE_FS_H
++++ b/include/trace/events/fs.h
+@@ -0,0 +1,66 @@
++#undef TRACE_SYSTEM
++#define TRACE_SYSTEM fs
++
++#if !defined(_TRACE_FS_H) || defined(TRACE_HEADER_MULTI_READ)
 +#define _TRACE_FS_H
 +
 +#include <linux/fs.h>
 +#include <linux/tracepoint.h>
 +
-+DECLARE_TRACE(do_sys_open,
-+	TPPROTO(struct file *filp, int flags, int mode, long fd),
-+		TPARGS(filp, flags, mode, fd));
-+
-+#endif
-diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
-index e2a4ff6..d2a1ec1 100644
---- a/kernel/trace/Kconfig
-+++ b/kernel/trace/Kconfig
-@@ -149,6 +149,15 @@ config CONTEXT_SWITCH_TRACER
- 	  This tracer gets called from the context switch and records
- 	  all switching of tasks.
- 
-+config OPEN_TRACER
-+	bool "Trace open() calls"
-+	depends on DEBUG_KERNEL
-+	select TRACING
-+	select MARKERS
-+	help
-+	  This tracer records open() syscalls. These calls are made when
-+	  files are accessed on disk.
-+
- config BOOT_TRACER
- 	bool "Trace boot initcalls"
- 	depends on DEBUG_KERNEL
-diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
-index 349d5a9..7f2a366 100644
---- a/kernel/trace/Makefile
-+++ b/kernel/trace/Makefile
-@@ -20,6 +20,7 @@ obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
- 
- obj-$(CONFIG_TRACING) += trace.o
- obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
-+obj-$(CONFIG_OPEN_TRACER) += trace_open.o
- obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o
- obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
- obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
-diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
-index 4d3d381..24c17d2 100644
---- a/kernel/trace/trace.h
-+++ b/kernel/trace/trace.h
-@@ -30,6 +30,7 @@ enum trace_type {
- 	TRACE_USER_STACK,
- 	TRACE_HW_BRANCHES,
- 	TRACE_POWER,
-+	TRACE_OPEN,
- 
- 	__TRACE_LAST_TYPE
- };
-diff --git a/kernel/trace/trace_open.c b/kernel/trace/trace_open.c
-new file mode 100644
-index 0000000..7153dfc
---- /dev/null
-+++ b/kernel/trace/trace_open.c
-@@ -0,0 +1,149 @@
 +/*
-+ * trace open calls
-+ * Copyright (C) 2009 Intel Corporation
-+ *
-+ * Based extensively on trace_sched_switch.c
-+ * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
-+ *
++ * Here we have a problem; the __string macro uses __dynamic_array,
++ * which requires the ability to know it's own length before we
++ * allocate the buffer - in the get_offsets_ call - which does not
++ * know the length of the resulting path we create in TP_fast_assign.
++ * So - give up and use a fixed length.
 + */
++TRACE_EVENT(do_sys_open,
 +
-+#include <linux/module.h>
-+#include <linux/fs.h>
-+#include <linux/debugfs.h>
-+#include <linux/kallsyms.h>
-+#include <linux/uaccess.h>
-+#include <linux/ftrace.h>
-+#include <trace/fs.h>
++	TP_PROTO(struct file *filp, int flags, int mode),
 +
-+#include "trace.h"
++	TP_ARGS(filp, flags, mode),
 +
++	TP_STRUCT__entry(
++		__file_p(	filename, filp			)
++		__field(	int, flags			)
++		__field(	int, mode			)
++	),
 +
-+static struct trace_array	*ctx_trace;
-+static int __read_mostly	open_trace_enabled;
-+static atomic_t			open_ref;
++	TP_fast_assign(
++		__assign_file_p(filename, filp);
++		__entry->flags = flags;
++		__entry->mode = mode;
++	),
 +
-+static void probe_do_sys_open(struct file *filp, int flags, int mode, long fd)
-+{
-+	char *buf;
-+	char *fname;
++	TP_printk("\"%s\" %x %o", __get_str(filename),
++		  __entry->flags, __entry->mode)
++);
 +
-+	if (!atomic_read(&open_ref))
-+		return;
++TRACE_EVENT(uselib,
++	TP_PROTO(struct file *filp),
++	TP_ARGS(filp),
++	TP_STRUCT__entry(
++		__file_p(filename, filp)
++	),
++	TP_fast_assign(
++		__assign_file_p(filename, filp);
++	),
++	TP_printk("\"%s\"", __get_str(filename))
++);
 +
-+	if (!open_trace_enabled)
-+		return;
++TRACE_EVENT(open_exec,
++	TP_PROTO(struct file *filp),
++	TP_ARGS(filp),
++	TP_STRUCT__entry(
++		__file_p(filename, filp)
++	),
++	TP_fast_assign(
++		__assign_file_p(filename, filp);
++	),
++	TP_printk("\"%s\"", __get_str(filename))
++);
 +
-+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
-+	if (!buf)
-+		return;
++#endif /* _TRACE_FS_H */
 +
-+	fname = d_path(&filp->f_path, buf, PAGE_SIZE);
-+	if (IS_ERR(fname))
-+		goto out;
++/* This part must be outside protection */
++#include <trace/define_trace.h>
+--- a/include/trace/ftrace.h
++++ b/include/trace/ftrace.h
+@@ -30,6 +30,9 @@
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, -1)
+ 
++#undef __file_p
++#define __file_p(item, src) __dynamic_array(char, item, -1)
 +
-+	ftrace_printk("%s: open(\"%s\", %d, %d) = %ld\n",
-+		current->comm, fname, flags, mode, fd);
+ #undef TP_STRUCT__entry
+ #define TP_STRUCT__entry(args...) args
+ 
+@@ -72,6 +75,10 @@
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, -1)
+ 
++#undef __file_p
++#define __file_p(item, src)			int item; \
++						int item##__size;
 +
-+out:
-+	kfree(buf);
-+}
+ #undef TRACE_EVENT
+ #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
+ 	struct ftrace_data_offsets_##call {				\
+@@ -131,6 +138,9 @@
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, -1)
+ 
++#undef __file_p
++#define __file_p(item, src) __dynamic_array(char, item, -1)
 +
-+static void open_trace_reset(struct trace_array *tr)
-+{
-+	tr->time_start = ftrace_now(tr->cpu);
-+	tracing_reset_online_cpus(tr);
-+}
+ #undef __entry
+ #define __entry REC
+ 
+@@ -289,6 +299,9 @@ ftrace_raw_output_##call(struct trace_it
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, -1)
+ 
++#undef __file_p
++#define __file_p(item, src) __dynamic_array(char, item, -1)
 +
-+static int open_trace_register(void)
-+{
-+	int ret;
+ #undef TRACE_EVENT
+ #define TRACE_EVENT(call, proto, args, tstruct, func, print)		\
+ int									\
+@@ -333,6 +346,14 @@ ftrace_define_fields_##call(void)					\
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, strlen(src) + 1)       \
+ 
++#undef __file_p
++#define __file_p(item, src)						\
++	__data_offsets->item = __data_size +				\
++			       offsetof(typeof(*entry), __data);	\
++	__data_offsets->item##__size = ftrace_file_name_len(src);	\
++	__data_size += __data_offsets->item##__size;
 +
-+	ret = register_trace_do_sys_open(probe_do_sys_open);
-+	if (ret) {
-+		pr_info("open trace: Could not activate tracepoint"
-+			" probe to do_open\n");
-+	}
 +
-+	return ret;
-+}
+ #undef TRACE_EVENT
+ #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
+ static inline int ftrace_get_offsets_##call(				\
+@@ -526,10 +547,17 @@ static void ftrace_profile_disable_##cal
+ #undef __string
+ #define __string(item, src) __dynamic_array(char, item, -1)       	\
+ 
++#undef __file_p
++#define __file_p(item, src) __dynamic_array(char, item, -1)       	\
 +
-+static void open_trace_unregister(void)
-+{
-+	unregister_trace_do_sys_open(probe_do_sys_open);
-+}
+ #undef __assign_str
+ #define __assign_str(dst, src)						\
+ 	strcpy(__get_str(dst), src);
+ 
++#undef __assign_file_p
++#define __assign_file_p(dst, src)					\
++	ftrace_assign_file(__get_str (dst), __data_offsets.dst##__size, src);
 +
-+static void open_trace_start(void)
+ #undef TRACE_EVENT
+ #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
+ 									\
+--- a/kernel/trace/trace_events.c
++++ b/kernel/trace/trace_events.c
+@@ -60,6 +60,40 @@ err:
+ }
+ EXPORT_SYMBOL_GPL(trace_define_field);
+ 
++/* file pointer helpers */
++int ftrace_file_name_len (const struct file *f)
 +{
-+	long ref;
++	/* This performs pretty terribly - obviously */
++	int len = 1;
++	char *buf, *fname;
 +
-+	ref = atomic_inc_return(&open_ref);
-+	if (ref == 1)
-+		open_trace_register();
++	if (!(buf = kzalloc(PAGE_SIZE, GFP_KERNEL)))
++		return len;
++	fname = d_path(&f->f_path, buf, PAGE_SIZE);
++	if (!IS_ERR (fname))
++		len += strlen (fname);
++	kfree(buf);
++	if (len >= 65536)
++		len = 65535;
++	return len;
 +}
 +
-+static void open_trace_stop(void)
++void ftrace_assign_file (char *dest, int dest_len, const struct file *f)
 +{
-+	long ref;
++	char *buf, *fname;
 +
-+	ref = atomic_dec_and_test(&open_ref);
-+	if (ref)
-+		open_trace_unregister();
-+}
++	dest[0] = '\0';
++	if (!(buf = kzalloc(PAGE_SIZE, GFP_KERNEL)))
++		return;
 +
-+void open_trace_start_cmdline_record(void)
-+{
-+	open_trace_start();
++	/* it would be nicer to write this directly into the
++	 * allocated buffer, but d_path doesn't like that */
++	fname = d_path(&f->f_path, buf, PAGE_SIZE);
++	if (!IS_ERR (fname))
++		strncpy (dest, fname, dest_len);
++	kfree(buf);
 +}
 +
-+void open_trace_stop_cmdline_record(void)
-+{
-+	open_trace_stop();
-+}
-+
-+static void open_start_trace(struct trace_array *tr)
-+{
-+	open_trace_reset(tr);
-+	open_trace_start_cmdline_record();
-+	open_trace_enabled = 1;
-+}
-+
-+static void open_stop_trace(struct trace_array *tr)
-+{
-+	open_trace_enabled = 0;
-+	open_trace_stop_cmdline_record();
-+}
-+
-+static int open_trace_init(struct trace_array *tr)
-+{
-+	ctx_trace = tr;
-+
-+	open_start_trace(tr);
-+	return 0;
-+}
-+
-+static void reset_open_trace(struct trace_array *tr)
-+{
-+	open_stop_trace(tr);
-+}
-+
-+static struct tracer open_trace __read_mostly =
-+{
-+	.name		= "open",
-+	.init		= open_trace_init,
-+	.reset		= reset_open_trace,
-+};
-+
-+__init static int init_open_trace(void)
-+{
-+	int ret = 0;
-+
-+	if (atomic_read(&open_ref))
-+		ret = open_trace_register();
-+	if (ret) {
-+		pr_info("error registering open trace\n");
-+		return ret;
-+	}
-+	return register_tracer(&open_trace);
-+}
-+device_initcall(init_open_trace);
-+
+ #ifdef CONFIG_MODULES
+ 
+ static void trace_destroy_fields(struct ftrace_event_call *call)
Index: Makefile
===================================================================
--- Makefile	(revision 38)
+++ Makefile	(working copy)
@@ -1,4 +1,5 @@
-CFLAGS ?= -Os -march=native -g
+CFLAGS ?= -O0 -g
+CFLAGS += -march=native
 PROGS = sreadahead
 
 VERSION = "1.0"
@@ -6,7 +7,7 @@
 all: $(PROGS)
 
 sreadahead: sreadahead.c Makefile
-	gcc $(CFLAGS) -lpthread -W sreadahead.c -o $@
+	gcc $(CFLAGS) -W sreadahead.c -o $@ -lpthread
 
 clean:
 	rm -f *~ $(PROGS)
