mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
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:
parent
79d955af71
commit
5bf6d1b927
@ -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);
|
||||||
|
@ -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 = {
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user