[PATCH 4/5] mtdram: Convert the flight recorder to a ring buffer.

Dirk Behme dirk.behme at de.bosch.com
Wed Dec 6 00:50:38 PST 2017


From: Manfred Spraul <manfred at colorfullife.com>

In flight recorder mode, all erase and write operations are logged.
The patch converts the internal buffer to a ring buffer, i.e.
it is not necessary anymore to read the complete buffer in one
syscall.

Signed-off-by: Manfred Spraul <manfred.spraul at de.bosch.com>
Cc: Manfred Spraul <manfred at colorfullife.com>
---
 drivers/mtd/devices/mtdram.c | 119 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 90 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index a6f5a656eb94..202696bc92ef 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -38,45 +38,101 @@ MODULE_PARM_DESC(fr_enabled, "Set the initial enabled/disabled status");
 #endif
 
 static char *fr_buffer;
-static int fr_pos;
+static int fr_head;
+static int fr_tail;
 static struct dentry *fr_dentry;
 
 static DEFINE_MUTEX(fr_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(fr_wait);
 
+static int nandrec_available_read(void)
+{
+	if (fr_head >= fr_tail)
+		return fr_head - fr_tail;
+
+	return fr_head + FR_BUFFER_TOTAL_SIZE - fr_tail;
+}
+
+static int nandrec_available_write(void)
+{
+	return FR_BUFFER_TOTAL_SIZE
+		- nandrec_available_read() - FR_BUFFER_MARGIN;
+}
+
+static void nandrec_advance_head(int count)
+{
+	WARN_ON(count > nandrec_available_write());
+
+	fr_head += count;
+	if (fr_head > FR_BUFFER_TOTAL_SIZE)
+		fr_head -= FR_BUFFER_TOTAL_SIZE;
+}
+
+static void nandrec_advance_tail(int count)
+{
+	WARN_ON(count > nandrec_available_read());
+
+	fr_tail += count;
+	if (fr_tail > FR_BUFFER_TOTAL_SIZE)
+		fr_tail -= FR_BUFFER_TOTAL_SIZE;
+
+	if (fr_tail == fr_head) {
+		fr_tail = 0;
+		fr_head = 0;
+	}
+}
+
 static ssize_t nandrec_read_file(struct file *file, char __user *user_buf,
 				 size_t count, loff_t *ppos)
 {
 	ssize_t r;
+	int max_data;
 
 	mutex_lock(&fr_mutex);
 
-	/* Every read must read all available data */
-	if (count < fr_pos) {
-		r = -EINVAL;
-		goto out_unlock;
-	}
+	max_data = nandrec_available_read();
 
-	if (fr_pos == 0) {
+	if (max_data == 0) {
 		r = 0;
 		goto out_unlock;
 	}
 
+	if (count > max_data)
+		count = max_data;
+
 	*ppos = 0;
 
 	r = debugfs_file_get(fr_dentry);
-	count = fr_pos;
 	if (likely(!r)) {
-		r = simple_read_from_buffer(user_buf, count, ppos, fr_buffer,
-					    FR_BUFFER_TOTAL_SIZE);
+		loff_t rpos;
+
+		rpos = fr_tail;
+		if (fr_tail + count > FR_BUFFER_TOTAL_SIZE) {
+			ssize_t tmp;
+
+			tmp = FR_BUFFER_TOTAL_SIZE - fr_tail;
+			r = simple_read_from_buffer(user_buf, tmp,
+						    &rpos, fr_buffer,
+						    FR_BUFFER_TOTAL_SIZE);
+			if (r == tmp) {
+				rpos = 0;
+				tmp = simple_read_from_buffer(user_buf + tmp,
+							count - tmp,
+							&rpos, fr_buffer,
+							FR_BUFFER_TOTAL_SIZE);
+				if (tmp > 0)
+					r += tmp;
+			}
+		} else {
+			r = simple_read_from_buffer(user_buf, count,
+						    &rpos, fr_buffer,
+						    FR_BUFFER_TOTAL_SIZE);
+		}
 		debugfs_file_put(fr_dentry);
 	}
 
-	/* Every read must read all available data */
-	WARN_ON(fr_pos != r);
-
-	/* Every read clears the kernel buffer */
-	fr_pos = 0;
+	if (r > 0)
+		nandrec_advance_tail(r);
 
 	/* if someone waits, wake him up */
 	if (waitqueue_active(&fr_wait))
@@ -137,31 +193,39 @@ static const struct file_operations fr_fops = {
 
 static void write_u32(u32 data)
 {
-	u32 *target = (u32 *)(fr_buffer + fr_pos);
+	u32 *target = (u32 *)(fr_buffer + fr_head);
 
 	if (fr_enabled) {
 		*target = cpu_to_le32(data);
-		fr_pos += round_up(sizeof(u32), 8);
+		nandrec_advance_head(round_up(sizeof(u32), 8));
 	}
 }
 
 static void write_u64(u64 data)
 {
-	u64 *target = (u64 *)(fr_buffer + fr_pos);
+	u64 *target = (u64 *)(fr_buffer + fr_head);
 
 	if (fr_enabled) {
 		*target = cpu_to_le64(data);
-		fr_pos += round_up(sizeof(u64), 8);
+		nandrec_advance_head(round_up(sizeof(u64), 8));
 	}
 }
 
 static void write_blob(const u_char *data, int len)
 {
-	u32 *target = (u32 *)(fr_buffer + fr_pos);
+	u32 *target = (u32 *)(fr_buffer + fr_head);
 
 	if (fr_enabled) {
-		memcpy(target, data, len);
-		fr_pos += round_up(len, 8);
+		if (fr_head + len > FR_BUFFER_TOTAL_SIZE) {
+			int p1;
+
+			p1 = FR_BUFFER_TOTAL_SIZE - fr_head;
+			memcpy(target, data, p1);
+			memcpy(fr_buffer, data + p1, len - p1);
+		} else {
+			memcpy(target, data, len);
+		}
+		nandrec_advance_head(round_up(len, 8));
 	}
 }
 
@@ -170,12 +234,11 @@ static void start_write(int size)
 	mutex_lock(&fr_mutex);
 
 	if (fr_enabled) {
-		while (fr_pos + size + 2*8 >=
-				FR_BUFFER_TOTAL_SIZE - FR_BUFFER_MARGIN) {
+		while (nandrec_available_write() < size + 2*8) {
 			DEFINE_WAIT(wait);
 
-			pr_info("%p: Waiting - write count %d, current %d.\n",
-				current, size, fr_pos);
+			pr_info("%p: Waiting - write count %d, current %d/%d.\n",
+				current, size, fr_tail, fr_head);
 
 			prepare_to_wait(&fr_wait, &wait, TASK_UNINTERRUPTIBLE);
 
@@ -191,10 +254,8 @@ static void start_write(int size)
 
 static void end_write(void)
 {
-	if (fr_enabled) {
+	if (fr_enabled)
 		write_u32(MAGIC_END);
-		WARN_ON(fr_pos > FR_BUFFER_TOTAL_SIZE - FR_BUFFER_MARGIN);
-	}
 	mutex_unlock(&fr_mutex);
 }
 
-- 
2.14.1




More information about the linux-mtd mailing list