pstore/pmsg: drop bounce buffer

Removing a bounce buffer copy operation in the pmsg driver path is
always better. We also gain in overall performance by not requesting
a vmalloc on every write as this can cause precious RT tasks, such
as user facing media operation, to stall while memory is being
reclaimed. Added a write_buf_user to the pstore functions, a backup
platform write_buf_user that uses the small buffer that is part of
the instance, and implemented a ramoops write_buf_user that only
supports PSTORE_TYPE_PMSG.

Signed-off-by: Mark Salyzyn <salyzyn@android.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Mark Salyzyn 2016-09-01 08:13:46 -07:00 committed by Kees Cook
parent 79d955af71
commit 5bf6d1b927
6 changed files with 119 additions and 36 deletions

View File

@ -623,6 +623,40 @@ static int pstore_write_compat(enum pstore_type_id type,
size, psi); size, psi);
} }
static int pstore_write_buf_user_compat(enum pstore_type_id type,
enum kmsg_dump_reason reason,
u64 *id, unsigned int part,
const char __user *buf,
bool compressed, size_t size,
struct pstore_info *psi)
{
unsigned long flags = 0;
size_t i, bufsize = size;
long ret = 0;
if (unlikely(!access_ok(VERIFY_READ, buf, size)))
return -EFAULT;
if (bufsize > psinfo->bufsize)
bufsize = psinfo->bufsize;
spin_lock_irqsave(&psinfo->buf_lock, flags);
for (i = 0; i < size; ) {
size_t c = min(size - i, bufsize);
ret = __copy_from_user(psinfo->buf, buf + i, c);
if (unlikely(ret != 0)) {
ret = -EFAULT;
break;
}
ret = psi->write_buf(type, reason, id, part, psinfo->buf,
compressed, c, psi);
if (unlikely(ret < 0))
break;
i += c;
}
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
return unlikely(ret < 0) ? ret : size;
}
/* /*
* platform specific persistent storage driver registers with * platform specific persistent storage driver registers with
* us here. If pstore is already mounted, call the platform * us here. If pstore is already mounted, call the platform
@ -645,6 +679,8 @@ int pstore_register(struct pstore_info *psi)
if (!psi->write) if (!psi->write)
psi->write = pstore_write_compat; psi->write = pstore_write_compat;
if (!psi->write_buf_user)
psi->write_buf_user = pstore_write_buf_user_compat;
psinfo = psi; psinfo = psi;
mutex_init(&psinfo->read_mutex); mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock); spin_unlock(&pstore_lock);

View File

@ -19,48 +19,25 @@
#include "internal.h" #include "internal.h"
static DEFINE_MUTEX(pmsg_lock); static DEFINE_MUTEX(pmsg_lock);
#define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE)
static ssize_t write_pmsg(struct file *file, const char __user *buf, static ssize_t write_pmsg(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
size_t i, buffer_size; u64 id;
char *buffer; int ret;
if (!count) if (!count)
return 0; return 0;
/* check outside lock, page in any data. write_buf_user also checks */
if (!access_ok(VERIFY_READ, buf, count)) if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT; return -EFAULT;
buffer_size = count;
if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE)
buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE;
buffer = vmalloc(buffer_size);
if (!buffer)
return -ENOMEM;
mutex_lock(&pmsg_lock); mutex_lock(&pmsg_lock);
for (i = 0; i < count; ) { ret = psinfo->write_buf_user(PSTORE_TYPE_PMSG, 0, &id, 0, buf, 0, count,
size_t c = min(count - i, buffer_size); psinfo);
u64 id;
long ret;
ret = __copy_from_user(buffer, buf + i, c);
if (unlikely(ret != 0)) {
mutex_unlock(&pmsg_lock);
vfree(buffer);
return -EFAULT;
}
psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c,
psinfo);
i += c;
}
mutex_unlock(&pmsg_lock); mutex_unlock(&pmsg_lock);
vfree(buffer); return ret ? ret : count;
return count;
} }
static const struct file_operations pmsg_fops = { static const struct file_operations pmsg_fops = {

View File

@ -331,6 +331,24 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
return 0; return 0;
} }
static int notrace ramoops_pstore_write_buf_user(enum pstore_type_id type,
enum kmsg_dump_reason reason,
u64 *id, unsigned int part,
const char __user *buf,
bool compressed, size_t size,
struct pstore_info *psi)
{
if (type == PSTORE_TYPE_PMSG) {
struct ramoops_context *cxt = psi->data;
if (!cxt->mprz)
return -ENOMEM;
return persistent_ram_write_user(cxt->mprz, buf, size);
}
return -EINVAL;
}
static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi) struct timespec time, struct pstore_info *psi)
{ {
@ -369,6 +387,7 @@ static struct ramoops_context oops_cxt = {
.open = ramoops_pstore_open, .open = ramoops_pstore_open,
.read = ramoops_pstore_read, .read = ramoops_pstore_read,
.write_buf = ramoops_pstore_write_buf, .write_buf = ramoops_pstore_write_buf,
.write_buf_user = ramoops_pstore_write_buf_user,
.erase = ramoops_pstore_erase, .erase = ramoops_pstore_erase,
}, },
}; };

View File

@ -17,15 +17,16 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/pstore_ram.h>
#include <linux/rslib.h> #include <linux/rslib.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/pstore_ram.h>
#include <asm/page.h> #include <asm/page.h>
struct persistent_ram_buffer { struct persistent_ram_buffer {
@ -267,6 +268,16 @@ static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
persistent_ram_update_ecc(prz, start, count); persistent_ram_update_ecc(prz, start, count);
} }
static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
const void __user *s, unsigned int start, unsigned int count)
{
struct persistent_ram_buffer *buffer = prz->buffer;
int ret = unlikely(__copy_from_user(buffer->data + start, s, count)) ?
-EFAULT : 0;
persistent_ram_update_ecc(prz, start, count);
return ret;
}
void persistent_ram_save_old(struct persistent_ram_zone *prz) void persistent_ram_save_old(struct persistent_ram_zone *prz)
{ {
struct persistent_ram_buffer *buffer = prz->buffer; struct persistent_ram_buffer *buffer = prz->buffer;
@ -320,6 +331,38 @@ int notrace persistent_ram_write(struct persistent_ram_zone *prz,
return count; return count;
} }
int notrace persistent_ram_write_user(struct persistent_ram_zone *prz,
const void __user *s, unsigned int count)
{
int rem, ret = 0, c = count;
size_t start;
if (unlikely(!access_ok(VERIFY_READ, s, count)))
return -EFAULT;
if (unlikely(c > prz->buffer_size)) {
s += c - prz->buffer_size;
c = prz->buffer_size;
}
buffer_size_add(prz, c);
start = buffer_start_add(prz, c);
rem = prz->buffer_size - start;
if (unlikely(rem < c)) {
ret = persistent_ram_update_user(prz, s, start, rem);
s += rem;
c -= rem;
start = 0;
}
if (likely(!ret))
ret = persistent_ram_update_user(prz, s, start, c);
persistent_ram_update_header_ecc(prz);
return unlikely(ret) ? ret : count;
}
size_t persistent_ram_old_size(struct persistent_ram_zone *prz) size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
{ {
return prz->old_log_size; return prz->old_log_size;

View File

@ -22,12 +22,13 @@
#ifndef _LINUX_PSTORE_H #ifndef _LINUX_PSTORE_H
#define _LINUX_PSTORE_H #define _LINUX_PSTORE_H
#include <linux/time.h> #include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/kmsg_dump.h> #include <linux/kmsg_dump.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/types.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/errno.h> #include <linux/time.h>
#include <linux/types.h>
/* types */ /* types */
enum pstore_type_id { enum pstore_type_id {
@ -68,6 +69,10 @@ struct pstore_info {
enum kmsg_dump_reason reason, u64 *id, enum kmsg_dump_reason reason, u64 *id,
unsigned int part, const char *buf, bool compressed, unsigned int part, const char *buf, bool compressed,
size_t size, struct pstore_info *psi); size_t size, struct pstore_info *psi);
int (*write_buf_user)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, const char __user *buf,
bool compressed, size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id, int (*erase)(enum pstore_type_id type, u64 id,
int count, struct timespec time, int count, struct timespec time,
struct pstore_info *psi); struct pstore_info *psi);

View File

@ -17,11 +17,12 @@
#ifndef __LINUX_PSTORE_RAM_H__ #ifndef __LINUX_PSTORE_RAM_H__
#define __LINUX_PSTORE_RAM_H__ #define __LINUX_PSTORE_RAM_H__
#include <linux/compiler.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/init.h>
struct persistent_ram_buffer; struct persistent_ram_buffer;
struct rs_control; struct rs_control;
@ -59,7 +60,9 @@ void persistent_ram_free(struct persistent_ram_zone *prz);
void persistent_ram_zap(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz);
int persistent_ram_write(struct persistent_ram_zone *prz, const void *s, int persistent_ram_write(struct persistent_ram_zone *prz, const void *s,
unsigned int count); unsigned int count);
int persistent_ram_write_user(struct persistent_ram_zone *prz,
const void __user *s, unsigned int count);
void persistent_ram_save_old(struct persistent_ram_zone *prz); void persistent_ram_save_old(struct persistent_ram_zone *prz);
size_t persistent_ram_old_size(struct persistent_ram_zone *prz); size_t persistent_ram_old_size(struct persistent_ram_zone *prz);