Hold the lock for longer when copying the kernel dmesg buffer.
This function would acquire and release a spinlock for every single byte.
If you used a large buffer size like 8MB, this could make the syslog()
function take more than a second to copy the data. On our device it was 1.5
seconds; with this change it's reduced to 0.17 seconds.
Change-Id: If63befbe18e4fc215f2e89f5c02dce87ea9064f2
Conflicts:
kernel/printk.c
diff --git a/kernel/printk.c b/kernel/printk.c
index 94c5fba..ad1a79b 100755
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -350,6 +350,7 @@
}
#endif
+#define COPY_SIZE 4096
/*
* Commands to do_syslog:
*
@@ -367,6 +368,7 @@
*/
int do_syslog(int type, char __user *buf, int len)
{
+ char *copybuf;
unsigned i, j, limit, count;
int do_clear = 0;
char c;
@@ -426,6 +428,11 @@
error = -EFAULT;
goto out;
}
+ copybuf = kmalloc(COPY_SIZE, GFP_KERNEL);
+ if (!copybuf) {
+ error = -ENOMEM;
+ goto out;
+ }
count = len;
if (count > log_buf_len)
count = log_buf_len;
@@ -436,7 +443,7 @@
logged_chars = 0;
limit = log_end;
/*
- * __put_user() could sleep, and while we sleep
+ * copy_to_user() could sleep, and while we sleep
* printk() could overwrite the messages
* we try to copy to user space. Therefore
* the messages are copied in reverse. <manfreds>
@@ -446,14 +453,26 @@
if (j + log_buf_len < log_end)
break;
c = LOG_BUF(j);
- spin_unlock_irq(&logbuf_lock);
- error = __put_user(c,&buf[count-1-i]);
- cond_resched();
- spin_lock_irq(&logbuf_lock);
+ copybuf[COPY_SIZE-1-(i % COPY_SIZE)] = c;
+ if ((i+1) % COPY_SIZE == 0) {
+ spin_unlock_irq(&logbuf_lock);
+ error = copy_to_user(&buf[count-1-i],
+ copybuf, COPY_SIZE);
+ cond_resched();
+ spin_lock_irq(&logbuf_lock);
+ }
}
spin_unlock_irq(&logbuf_lock);
- if (error)
- break;
+ if (!error) {
+ /* in case copybuf was only partially filled */
+ error = copy_to_user(&buf[count-i],
+ copybuf + COPY_SIZE - (i % COPY_SIZE),
+ i % COPY_SIZE);
+ }
+ if (error) {
+ error = -EFAULT;
+ goto copy_done;
+ }
error = i;
if (i != count) {
int offset = count-error;
@@ -467,6 +486,8 @@
cond_resched();
}
}
+copy_done:
+ kfree(copybuf);
break;
case 5: /* Clear ring buffer */
logged_chars = 0;