Program to create bootcache area

Changes to bootcache program to match changes that have been
made to the bootcache device mapper.

BUG=chromium-os:25441
TEST=created bootcache and booted system.

Change-Id: Idc62d4cea72d69f4c202460400219184916231ee
Reviewed-on: https://gerrit.chromium.org/gerrit/31761
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
Commit-Ready: Paul Taysom <taysom@chromium.org>
Tested-by: Paul Taysom <taysom@chromium.org>
diff --git a/bootcache.c b/bootcache.c
index 515bde2..5fa9a8f 100644
--- a/bootcache.c
+++ b/bootcache.c
@@ -12,7 +12,10 @@
  * conjunction with dm-bootcache device mapper to coalesce
  * the blocks used during boot.
  *
- * bootcache [-t] <device-name>
+ * Sizes and offsets are measured in 512 byte sectors.
+ * Space is allocated in CHUNK_SIZE chunks.
+ *
+ * bootcache [-t] <device-name> <raw-partition>
  *
  *   -t - for testing - looks in a different place for
  *        information files.
@@ -21,14 +24,14 @@
  *        prefix.
  *
  * Files:
- * 1. Device - /dev/dm-0 - Where the blocks to be cached
- *             are stored. Both the original and cached
- *             copy.
+ * 1. Device - <raw-partition> - Where the blocks to be
+ *             cached are stored. Both the original and
+ *             cached copy.
  * 2. Header - /sys/kernel/debug/dm-bootcache/dm-0/header
  *             Header for the boot cache. It contains the
  *             information the bootcache utility will need
  *             to create the bootcache.
- * 3. Trace  - /sys/kernel/debug/dm-bootcache/dm-0/trace
+ * 3. Trace  - /sys/kernel/debug/dm-bootcache/dm-0/blocktrace
  *             Trace of files read during boot
  * 4. Valid  - /sys/kernel/debug/dm-bootcache/dm-0/valid
  *             Returns "1" if cache is valid
@@ -41,14 +44,16 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/user.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdbool.h>
-#include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 
 #include "dm-bootcache.h"
@@ -56,30 +61,41 @@
 typedef uint64_t u64;
 typedef uint32_t u32;
 
-#define MAX_BLOCKS 128
+#define CHUNK_SIZE 4096	/* todo(taysom) should get from bootcache_hdr */
+#define SECTOR_SHIFT 9
+#define MAX_CHUNKS 128
 #define MAX_FILE_NAME 256
+#define SECTORS_PER_CHUNK (CHUNK_SIZE >> SECTOR_SHIFT)
+#define MAX_MSG 1024
 
-static struct Bootcache_hdr Header;
+static struct bootcache_hdr Header;
 static struct {
-	struct Trace *tr;
+	struct bootcache_trace *tr;
 	int num;
 } Trace;
 
 static const char Progname[] = "bootcache";
 
-static char Device_file[MAX_FILE_NAME];
 static char Valid_file[MAX_FILE_NAME];
 static char Free_file[MAX_FILE_NAME];
 static char Header_file[MAX_FILE_NAME];
-static char Trace_file[MAX_FILE_NAME];
+static char Blocktrace_file[MAX_FILE_NAME];
 
-static u64 Header_block;
 static u64 Trace_start;
 static u64 Cache_start;
 
 #define fatal(fmt, ...)	pr_fatal(__FILE__, __FUNCTION__, __LINE__, \
 				fmt, ## __VA_ARGS__)
 
+#define PRs(_x) printf("|%s<%d> %s %s\n", __FUNCTION__, __LINE__, \
+			# _x, _x);
+
+#define PRd(_x) printf("|%s<%d> %s %lld\n", __FUNCTION__, __LINE__, \
+			# _x, (unsigned long long)(_x));
+
+#define PRx(_x) printf("|%s<%d> %s %llx\n", __FUNCTION__, __LINE__, \
+			# _x, (unsigned long long)(_x));
+
 /* pr_fatal: print error message and exit */
 static void pr_fatal(
 	const char *file,
@@ -87,20 +103,28 @@
 	int line,
 	const char *fmt, ...)
 {
+	char msg[MAX_MSG];
 	va_list args;
+	int n = MAX_MSG;
+	int i = 0;
+	int r;
 
 	fflush(stdout);
-	fprintf(stderr, "Fatal %s %s:%s<%d> ", Progname, file, func, line);
-	if (fmt) {
+	r = snprintf(msg, n, "Fatal %s %s:%s<%d> ", Progname, file, func, line);
+	n -= r;
+	i += r;
+	if (n && fmt) {
 		va_start(args, fmt);
-		vfprintf(stderr, fmt, args);
+		r = vsnprintf(&msg[i], n, fmt, args);
+		n -= r;
+		i += r;
 		va_end(args);
 
-		if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
-			fprintf(stderr, " %s<%d>", strerror(errno), errno);
+		if (n && fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
+			snprintf(&msg[i], n, " %s<%d>", strerror(errno), errno);
 		}
 	}
-	fprintf(stderr, "\n");
+	syslog(LOG_ERR, "%s\n", msg);
 	exit(2); /* conventional value for failed execution */
 }
 
@@ -153,14 +177,14 @@
 	void *buf;
 	int rc;
 
-	rc = posix_memalign(&buf, BLK_SIZE, npages * BLK_SIZE);
+	rc = posix_memalign(&buf, CHUNK_SIZE, npages * CHUNK_SIZE);
 	if (rc) {
 		fatal("posix_memalign rc=%d", rc);
 	}
 	return buf;
 }
 
-static u64 num_blocks_in_cache(void)
+static u64 num_sectors_in_cache(void)
 {
 	int i;
 	u64 sum = 0;
@@ -171,33 +195,41 @@
 	return sum;
 }
 
-static void compute_sections(void)
+static u64 num_meta_sectors(void)
 {
-	Header.num_blks_meta = BLK_ALIGN(Trace.num * sizeof(*Trace.tr));
-	Header.num_blks_data = num_blocks_in_cache();
-	Header_block = Header.blkno;
-	Trace_start = Header_block + 1;
-	Cache_start = Trace_start + Header.num_blks_meta;
+	u64 num_bytes = Trace.num * sizeof(*Trace.tr);
+
+	/* Align to page boundary then convert to sectors */
+	return ((num_bytes + CHUNK_SIZE - 1) / CHUNK_SIZE) * SECTORS_PER_CHUNK;
 }
 
-static void copy_trace(int dst, int src, struct Trace tr, void *buf)
+static void compute_sections(void)
+{
+	Header.num_trace_recs = Trace.num;
+	Header.sectors_meta = num_meta_sectors();
+	Header.sectors_data = num_sectors_in_cache();
+	Trace_start = Header.sector + SECTORS_PER_CHUNK;
+	Cache_start = Trace_start + Header.sectors_meta;
+}
+
+static void copy_trace(int dst, int src, struct bootcache_trace tr, void *buf)
 {
 	u64 n;
 	u64 remainder;
 	u64 offset;
 	int rc;
 
-	offset    = tr.blkno << BLK_SHIFT;
-	remainder = tr.count << BLK_SHIFT;
-	n = BLK_SIZE * MAX_BLOCKS;
+	offset    = tr.sector << SECTOR_SHIFT;
+	remainder = tr.count << SECTOR_SHIFT;
+	n = MAX_CHUNKS * CHUNK_SIZE;
 	while (remainder) {
 		if (n > remainder) {
 			n = remainder;
 		}
 		rc = pread(src, buf, n, offset);
 		if (rc < 0) {
-			fatal("pread trace offset=%llu num blocks=%llu",
-				offset >> BLK_SHIFT, n >> BLK_SHIFT);
+			fatal("pread trace offset=%llu num sectors=%llu:",
+				offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT);
 		}
 		if (rc != n) {
 			fatal("pread read only %u bytes expected %llu",
@@ -205,8 +237,8 @@
 		}
 		rc = write(dst, buf, n);
 		if (rc < 0) {
-			fatal("write trace offset=%llu num blocks=%llu",
-				offset >> BLK_SHIFT, n >> BLK_SHIFT);
+			fatal("write trace offset=%llu num sectors=%llu:",
+				offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT);
 		}
 		if (rc != n) {
 			fatal("write wrote only %u bytes expected %llu",
@@ -224,8 +256,9 @@
 
 	int src = open(device, O_RDONLY);
 	int dst = open(device, O_WRONLY);
-	void *buf = malloc_buf(MAX_BLOCKS);
-	rc = lseek(dst, Cache_start << BLK_SHIFT, SEEK_SET);
+	void *buf = malloc_buf(MAX_CHUNKS);
+
+	rc = lseek(dst, Cache_start << SECTOR_SHIFT, SEEK_SET);
 	if (rc == -1) {
 		fatal("lseek for cache start:");
 	}
@@ -238,28 +271,31 @@
 	eclose(src);
 }
 
-static void dump_trace(struct Trace *tr, int num_recs)
+static void dump_trace()
 {
+	struct bootcache_trace *tr = Trace.tr;
 	int i;
 
-	for (i = 0; i < num_recs; i++, tr++) {
-		printf("%llu %llu %llu\n", tr->blkno, tr->count, tr->ino);
+	if (0) {
+		for (i = 0; i < Trace.num; i++, tr++) {
+			printf("%llu %llu %llu\n", tr->sector, tr->count, tr->ino);
+		}
 	}
 }
 
 /*
- * Because we are reading a pseudo file, we scan it to
- * see how big it is.
+ * Because we are reading a pseudo file in sysfs,
+ * we scan it to see how big it is.
  */
-static u64 num_traces(const char *file)
+static u64 num_bytes(const char *file)
 {
-	struct Trace trace[1024];
+	char buf[CHUNK_SIZE];
 	ssize_t rc;
 	u64 sum = 0;
 
 	int fd = eopen(file, O_RDONLY);
 	for (;;) {
-		rc = read(fd, trace, sizeof(trace));
+		rc = read(fd, buf, sizeof(buf));
 		if (rc == -1)
 			fatal("read %s:", file);
 		if (rc == 0)
@@ -267,23 +303,39 @@
 		sum += rc;
 	}
 	eclose(fd);
-	return sum / sizeof(struct Trace);
+	return sum;
 }
 
 static void read_trace(const char *file)
 {
-	u64 n = num_traces(file);
+	/*
+	 * Because this is a sysfs file, we have to read it to get
+	 * its size. Even if more data is appended to the file, we
+	 * don't care, we just want the data up to this point in
+	 * time.
+	 */
+	u64 n = num_bytes(file);
 	ssize_t rc;
 	int fd;
+	char *b;
 
-	Trace.tr  = emalloc(n * sizeof(struct Trace));
-	Trace.num = n;
+	Trace.tr  = emalloc(n);
+	Trace.num = n / sizeof(struct bootcache_trace);
 	fd = eopen(file, O_RDONLY);
-	rc = read(fd, Trace.tr, n * sizeof(struct Trace));
-	if (rc == -1) {
-		fatal("read %s:", file);
+	/*
+	 * Because sysfs only returns a page at a time,
+	 * will need to do the read in a loop.
+	 */
+	for (b = (char *)Trace.tr; n; n -= rc, b += rc) {
+		rc = read(fd, b, n);
+		if (rc == -1) {
+			fatal("read %s:", file);
+		}
+		if (rc == 0) {
+			fatal("trying to read %lld bytes", n);
+		}
 	}
-	dump_trace(Trace.tr, n);
+	dump_trace();
 	eclose(fd);
 }
 
@@ -319,7 +371,7 @@
 	int rc;
 
 	fd = eopen(file, O_WRONLY);
-	rc = pwrite(fd, &Header, sizeof(Header), Header_block << BLK_SHIFT);
+	rc = pwrite(fd, &Header, sizeof(Header), Header.sector << SECTOR_SHIFT);
 	if (rc != sizeof(Header)) {
 		fatal("pwrite %s rc=%d:", file, rc);
 	}
@@ -334,7 +386,7 @@
 	ssize_t size = Trace.num * sizeof(*Trace.tr);
 
 	fd = eopen(file, O_WRONLY);
-	rc = pwrite(fd, Trace.tr, size, Trace_start);
+	rc = pwrite(fd, Trace.tr, size, Trace_start << SECTOR_SHIFT);
 	if (rc != size) {
 		fatal("pwrite %s rc=%ld size=%ld:", file, rc, size);
 	}
@@ -380,96 +432,77 @@
 	return buf[0] == '1';
 }
 
-static void gen_file_name(char *file_name, int size,
-			const char *fmt, const char *name)
+static void gen_file_name(char *file_name, int size, const char *fmt,
+				const char *prefix, const char *name)
 {
 	int rc;
 
-	rc = snprintf(file_name, size, fmt, name);
+	rc = snprintf(file_name, size, fmt, prefix, name);
 	if (rc >= size) {
 		fatal("Name too long %s", name);
 	}
 }
 
-static void gen_file_names(const char *name)
+static void gen_file_names(const char *fmt, const char *device_mapper)
 {
-	gen_file_name(Device_file, sizeof(Device_file), "/dev/%s", name);
 	gen_file_name(Valid_file, sizeof(Valid_file),
-			"/sys/kernel/debug/dm-bootcache/%s/valid", name);
+			fmt, device_mapper, "valid");
 	gen_file_name(Free_file, sizeof(Free_file),
-			"/sys/kernel/debug/dm-bootcache/%s/free", name);
+			fmt, device_mapper, "free");
 	gen_file_name(Header_file, sizeof(Header_file),
-			"/sys/kernel/debug/dm-bootcache/%s/header", name);
-	gen_file_name(Trace_file, sizeof(Trace_file),
-			"/sys/kernel/debug/dm-bootcache/%s/trace", name);
-}
-
-static void test_file_names(const char *name)
-{
-	gen_file_name(Device_file, sizeof(Device_file),
-			"/tmp/%s/dev", name);
-	gen_file_name(Valid_file, sizeof(Valid_file),
-			"/tmp/%s/valid", name);
-	gen_file_name(Free_file, sizeof(Free_file),
-			"/tmp/%s/free", name);
-	gen_file_name(Header_file, sizeof(Header_file),
-			"/tmp/%s/header", name);
-	gen_file_name(Trace_file, sizeof(Trace_file),
-			"/tmp/%s/trace", name);
+			fmt, device_mapper, "header");
+	gen_file_name(Blocktrace_file, sizeof(Blocktrace_file),
+			fmt, device_mapper, "blocktrace");
 }
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: %s [-t] <name>\n"
-			"  e.g %s dm-0\n",
+	fprintf(stderr, "Usage: %s [-t]"
+			" <device mapper> <raw partition>\n"
+			"  e.g %s dm-0 /dev/sda3\n",
 			Progname, Progname);
 	exit(2);
 }
 
 int main(int argc, char *argv[])
 {
-	bool test = false;
-	char *name = NULL;
+	char *device_mapper = NULL;
+	char *raw_partition = NULL;
 
+	openlog(Progname, LOG_PERROR | LOG_CONS | LOG_PID, 0);
+	syslog(LOG_ERR, "started\n");
 	for (;;) {
 		int c;
 
-		c = getopt(argc, argv, "t?");
+		c = getopt(argc, argv, "?");
 		if (c == -1)
 			break;
 		switch (c) {
-		case 't':
-			test = true;
-			break;
 		case '?':
 		default:
 			usage();
 			break;
 		}
 	}
-	if (optind >= argc) {
+	if (optind+2 != argc) {
 		usage();
 	}
-	name = argv[optind];
-	if (test) {
-		test_file_names(name);
-	} else {
-		gen_file_names(name);
-	}
-	if (is_valid(Valid_file)) {
-		/* Because the boot cache is valid, the block
-		 * traces are not kept so the boot cache can't
-		 * be rebuilt. To force a rebuild of the cache,
-		 * zero the header.
+	device_mapper = argv[optind];
+	raw_partition = argv[optind + 1];
+	gen_file_names("/sys/devices/virtual/block/%s/dm/%s",
+			device_mapper);
+	if (!is_valid(Valid_file)) {
+		/*
+		 * Rebuild the bootcache
 		 */
-		return 0;
+		read_header(Header_file);
+		read_trace(Blocktrace_file);
+		compute_sections();
+		copy_blocks(raw_partition);
+		write_trace(raw_partition);
+		write_header(raw_partition);
 	}
-	read_header(Header_file);
-	read_trace(Trace_file);
-	compute_sections();
-	copy_blocks(Device_file);
-	write_trace(Device_file);
-	write_header(Device_file);
 	free_bootcache(Free_file);
+	syslog(LOG_ERR, "done\n");
 	return 0;
 }
diff --git a/bootcache.conf b/bootcache.conf
new file mode 100644
index 0000000..27d00db
--- /dev/null
+++ b/bootcache.conf
@@ -0,0 +1,19 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description     "Start the bootcache process"
+author          "chromium-os-dev@chromium.org"
+
+# Starts the bootcache deamon that either creates the bootcache
+# or cleans it up after it is used.
+start on started system-services
+
+pre-start script
+  sleep 5  # Give a little extra time for chrome
+end script
+
+script
+  device=$(rootdev -s)
+  exec ionice -c3 bootcache dm-0 $device
+end script
diff --git a/dm-bootcache.h b/dm-bootcache.h
index af12df8..536911f 100644
--- a/dm-bootcache.h
+++ b/dm-bootcache.h
@@ -1,42 +1,44 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
+/*
+ * Copyright 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  */
 
-#ifndef _BOOTCACHE_H
-#define _BOOTCACHE_H
+#ifndef DM_BOOTCACHE_H
+#define DM_BOOTCACHE_H
 
 #include <linux/types.h>
 
-enum {	BLK_SHIFT = 12,
-	BLK_SIZE = 1<<BLK_SHIFT,
-	BLK_MASK = BLK_SIZE - 1,
-	SECTOR_BLOCK_SHIFT = 3,
-	BOOTCACHE_MAGIC = 1651470196,
+enum {	BOOTCACHE_MAGIC = 1651470196,
 	BOOTCACHE_VERSION = 2,
 	MAX_SIGNATURE = 256
 };
 
-
-#define BLK_ALIGN(_x)	(((_x) + BLK_MASK) >> BLK_SHIFT)
-
-struct Trace {
-	__u64	blkno;	/* Block number for data */
-	__u64	count;	/* Number of blocks in request */
-	__u64	ino;	/* Used for anaysis of traces */
+struct bootcache_trace {
+	__u64	sector;	/* Sector offset */
+	__u64	count;	/* Number of blocks traced */
+	__u64	ino;	/* Inode number of file */
 };
 
-struct Bootcache_hdr {
-	__u64	blkno;		/* Block number for where header is stored */
+struct bootcache_hdr {
+	__u64	sector;		/* Sector offset where header is stored */
 	__u32	magic;		/* Magic number */
 	__u32	version;	/* Verion of boot cache */
 	__u32	state;		/* Curent state */
-	__u32	num_blks_meta;	/* Size of trace data on disk */
-	__u32	num_blks_data;	/* Size of the data area */
-	char	date[12];	/* Date and time bootcache was compiled */
-	char	time[12];
+	__u32	num_trace_recs;	/* Number of trace reords */
+	__u32	sectors_meta;	/* Size of trace data on disk in sectors*/
+	__u32	sectors_data;	/* Size of the data area in sectors*/
 	__u32	max_sectors;	/* Max sectors that can to read */
 	__u32	max_hw_sectors;	/* Max hardware sectore that can be read */
+	char	date[12];	/* Date and time dm-bootcache was compiled */
+	char	time[12];
 	char	signature[MAX_SIGNATURE];
 };