Commit 52281b38 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pstore-v4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull pstore updates from Kees Cook:
 "Improvements and fixes to pstore subsystem:

   - add additional checks for bad platform data

   - remove bounce buffer in console writer

   - protect read/unlink race with a mutex

   - correctly give up during dump locking failures

   - increase ftrace bandwidth by splitting ftrace buffers per CPU"

* tag 'pstore-v4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  ramoops: add pdata NULL check to ramoops_probe
  pstore: Convert console write to use ->write_buf
  pstore: Protect unlink with read_mutex
  pstore: Use global ftrace filters for function trace filtering
  ftrace: Provide API to use global filtering for ftrace ops
  pstore: Clarify context field przs as dprzs
  pstore: improve error report for failed setup
  pstore: Merge per-CPU ftrace records into one
  pstore: Add ftrace timestamp counter
  ramoops: Split ftrace buffer space into per-CPU zones
  pstore: Make ramoops_init_przs generic for other prz arrays
  pstore: Allow prz to control need for locking
  pstore: Warn on PSTORE_TYPE_PMSG using deprecated function
  pstore: Make spinlock per zone instead of global
  pstore: Actually give up during locking failure
parents daf34710 fc46d4e4
...@@ -46,3 +46,6 @@ Optional properties: ...@@ -46,3 +46,6 @@ Optional properties:
(defaults to buffered mappings) (defaults to buffered mappings)
- no-dump-oops: if present, only dump panics (defaults to panics and oops) - no-dump-oops: if present, only dump panics (defaults to panics and oops)
- flags: if present, pass ramoops behavioral flags (defaults to 0,
see include/linux/pstore_ram.h RAMOOPS_FLAG_* for flag values).
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
#include <asm/barrier.h> #include <asm/barrier.h>
#include "internal.h" #include "internal.h"
/* This doesn't need to be atomic: speed is chosen over correctness here. */
static u64 pstore_ftrace_stamp;
static void notrace pstore_ftrace_call(unsigned long ip, static void notrace pstore_ftrace_call(unsigned long ip,
unsigned long parent_ip, unsigned long parent_ip,
struct ftrace_ops *op, struct ftrace_ops *op,
...@@ -42,6 +45,7 @@ static void notrace pstore_ftrace_call(unsigned long ip, ...@@ -42,6 +45,7 @@ static void notrace pstore_ftrace_call(unsigned long ip,
rec.ip = ip; rec.ip = ip;
rec.parent_ip = parent_ip; rec.parent_ip = parent_ip;
pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++);
pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());
psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec, psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec,
0, sizeof(rec), psinfo); 0, sizeof(rec), psinfo);
...@@ -71,10 +75,13 @@ static ssize_t pstore_ftrace_knob_write(struct file *f, const char __user *buf, ...@@ -71,10 +75,13 @@ static ssize_t pstore_ftrace_knob_write(struct file *f, const char __user *buf,
if (!on ^ pstore_ftrace_enabled) if (!on ^ pstore_ftrace_enabled)
goto out; goto out;
if (on) if (on) {
ftrace_ops_set_global_filter(&pstore_ftrace_ops);
ret = register_ftrace_function(&pstore_ftrace_ops); ret = register_ftrace_function(&pstore_ftrace_ops);
else } else {
ret = unregister_ftrace_function(&pstore_ftrace_ops); ret = unregister_ftrace_function(&pstore_ftrace_ops);
}
if (ret) { if (ret) {
pr_err("%s: unable to %sregister ftrace ops: %zd\n", pr_err("%s: unable to %sregister ftrace ops: %zd\n",
__func__, on ? "" : "un", ret); __func__, on ? "" : "un", ret);
......
...@@ -107,9 +107,11 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v) ...@@ -107,9 +107,11 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
struct pstore_ftrace_seq_data *data = v; struct pstore_ftrace_seq_data *data = v;
struct pstore_ftrace_record *rec = (void *)(ps->data + data->off); struct pstore_ftrace_record *rec = (void *)(ps->data + data->off);
seq_printf(s, "%d %08lx %08lx %pf <- %pF\n", seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n",
pstore_ftrace_decode_cpu(rec), rec->ip, rec->parent_ip, pstore_ftrace_decode_cpu(rec),
(void *)rec->ip, (void *)rec->parent_ip); pstore_ftrace_read_timestamp(rec),
rec->ip, rec->parent_ip, (void *)rec->ip,
(void *)rec->parent_ip);
return 0; return 0;
} }
...@@ -197,11 +199,14 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry) ...@@ -197,11 +199,14 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
if (err) if (err)
return err; return err;
if (p->psi->erase) if (p->psi->erase) {
mutex_lock(&p->psi->read_mutex);
p->psi->erase(p->type, p->id, p->count, p->psi->erase(p->type, p->id, p->count,
d_inode(dentry)->i_ctime, p->psi); d_inode(dentry)->i_ctime, p->psi);
else mutex_unlock(&p->psi->read_mutex);
} else {
return -EPERM; return -EPERM;
}
return simple_unlink(dir, dentry); return simple_unlink(dir, dentry);
} }
......
...@@ -5,40 +5,6 @@ ...@@ -5,40 +5,6 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB)
#define PSTORE_CPU_IN_IP 0x1
#elif NR_CPUS <= 4 && defined(CONFIG_ARM)
#define PSTORE_CPU_IN_IP 0x3
#endif
struct pstore_ftrace_record {
unsigned long ip;
unsigned long parent_ip;
#ifndef PSTORE_CPU_IN_IP
unsigned int cpu;
#endif
};
static inline void
pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
{
#ifndef PSTORE_CPU_IN_IP
rec->cpu = cpu;
#else
rec->ip |= cpu;
#endif
}
static inline unsigned int
pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
{
#ifndef PSTORE_CPU_IN_IP
return rec->cpu;
#else
return rec->ip & PSTORE_CPU_IN_IP;
#endif
}
#ifdef CONFIG_PSTORE_FTRACE #ifdef CONFIG_PSTORE_FTRACE
extern void pstore_register_ftrace(void); extern void pstore_register_ftrace(void);
extern void pstore_unregister_ftrace(void); extern void pstore_unregister_ftrace(void);
......
...@@ -493,6 +493,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -493,6 +493,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
if (!is_locked) { if (!is_locked) {
pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
, in_nmi() ? "NMI" : why); , in_nmi() ? "NMI" : why);
return;
} }
} else { } else {
spin_lock_irqsave(&psinfo->buf_lock, flags); spin_lock_irqsave(&psinfo->buf_lock, flags);
...@@ -584,8 +585,8 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) ...@@ -584,8 +585,8 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
} else { } else {
spin_lock_irqsave(&psinfo->buf_lock, flags); spin_lock_irqsave(&psinfo->buf_lock, flags);
} }
memcpy(psinfo->buf, s, c); psinfo->write_buf(PSTORE_TYPE_CONSOLE, 0, &id, 0,
psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, 0, 0, c, psinfo); s, 0, c, psinfo);
spin_unlock_irqrestore(&psinfo->buf_lock, flags); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
s += c; s += c;
c = e - s; c = e - s;
......
This diff is collapsed.
...@@ -48,16 +48,15 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz) ...@@ -48,16 +48,15 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz)
return atomic_read(&prz->buffer->start); return atomic_read(&prz->buffer->start);
} }
static DEFINE_RAW_SPINLOCK(buffer_lock);
/* increase and wrap the start pointer, returning the old value */ /* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{ {
int old; int old;
int new; int new;
unsigned long flags; unsigned long flags = 0;
raw_spin_lock_irqsave(&buffer_lock, flags); if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->start); old = atomic_read(&prz->buffer->start);
new = old + a; new = old + a;
...@@ -65,7 +64,8 @@ static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) ...@@ -65,7 +64,8 @@ static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
new -= prz->buffer_size; new -= prz->buffer_size;
atomic_set(&prz->buffer->start, new); atomic_set(&prz->buffer->start, new);
raw_spin_unlock_irqrestore(&buffer_lock, flags); if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
return old; return old;
} }
...@@ -75,9 +75,10 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) ...@@ -75,9 +75,10 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
{ {
size_t old; size_t old;
size_t new; size_t new;
unsigned long flags; unsigned long flags = 0;
raw_spin_lock_irqsave(&buffer_lock, flags); if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_lock_irqsave(&prz->buffer_lock, flags);
old = atomic_read(&prz->buffer->size); old = atomic_read(&prz->buffer->size);
if (old == prz->buffer_size) if (old == prz->buffer_size)
...@@ -89,7 +90,8 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) ...@@ -89,7 +90,8 @@ static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
atomic_set(&prz->buffer->size, new); atomic_set(&prz->buffer->size, new);
exit: exit:
raw_spin_unlock_irqrestore(&buffer_lock, flags); if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
} }
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
...@@ -465,7 +467,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, ...@@ -465,7 +467,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
} }
static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig, static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
struct persistent_ram_ecc_info *ecc_info) struct persistent_ram_ecc_info *ecc_info,
unsigned long flags)
{ {
int ret; int ret;
...@@ -493,6 +496,8 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig, ...@@ -493,6 +496,8 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
prz->buffer->sig = sig; prz->buffer->sig = sig;
persistent_ram_zap(prz); persistent_ram_zap(prz);
prz->buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(buffer_lock);
prz->flags = flags;
return 0; return 0;
} }
...@@ -517,7 +522,7 @@ void persistent_ram_free(struct persistent_ram_zone *prz) ...@@ -517,7 +522,7 @@ void persistent_ram_free(struct persistent_ram_zone *prz)
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info, u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype) unsigned int memtype, u32 flags)
{ {
struct persistent_ram_zone *prz; struct persistent_ram_zone *prz;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -532,7 +537,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, ...@@ -532,7 +537,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
if (ret) if (ret)
goto err; goto err;
ret = persistent_ram_post_init(prz, sig, ecc_info); ret = persistent_ram_post_init(prz, sig, ecc_info, flags);
if (ret) if (ret)
goto err; goto err;
......
...@@ -398,6 +398,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, ...@@ -398,6 +398,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
void ftrace_set_global_filter(unsigned char *buf, int len, int reset); void ftrace_set_global_filter(unsigned char *buf, int len, int reset);
void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); void ftrace_set_global_notrace(unsigned char *buf, int len, int reset);
void ftrace_free_filter(struct ftrace_ops *ops); void ftrace_free_filter(struct ftrace_ops *ops);
void ftrace_ops_set_global_filter(struct ftrace_ops *ops);
int register_ftrace_command(struct ftrace_func_command *cmd); int register_ftrace_command(struct ftrace_func_command *cmd);
int unregister_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd);
...@@ -645,6 +646,7 @@ static inline unsigned long ftrace_location(unsigned long ip) ...@@ -645,6 +646,7 @@ static inline unsigned long ftrace_location(unsigned long ip)
#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_free_filter(ops) do { } while (0) #define ftrace_free_filter(ops) do { } while (0)
#define ftrace_ops_set_global_filter(ops) do { } while (0)
static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf,
size_t cnt, loff_t *ppos) { return -ENODEV; } size_t cnt, loff_t *ppos) { return -ENODEV; }
......
...@@ -89,4 +89,80 @@ extern int pstore_register(struct pstore_info *); ...@@ -89,4 +89,80 @@ extern int pstore_register(struct pstore_info *);
extern void pstore_unregister(struct pstore_info *); extern void pstore_unregister(struct pstore_info *);
extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason); extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
struct pstore_ftrace_record {
unsigned long ip;
unsigned long parent_ip;
u64 ts;
};
/*
* ftrace related stuff: Both backends and frontends need these so expose
* them here.
*/
#if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB)
#define PSTORE_CPU_IN_IP 0x1
#elif NR_CPUS <= 4 && defined(CONFIG_ARM)
#define PSTORE_CPU_IN_IP 0x3
#endif
#define TS_CPU_SHIFT 8
#define TS_CPU_MASK (BIT(TS_CPU_SHIFT) - 1)
/*
* If CPU number can be stored in IP, store it there, otherwise store it in
* the time stamp. This means more timestamp resolution is available when
* the CPU can be stored in the IP.
*/
#ifdef PSTORE_CPU_IN_IP
static inline void
pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
{
rec->ip |= cpu;
}
static inline unsigned int
pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
{
return rec->ip & PSTORE_CPU_IN_IP;
}
static inline u64
pstore_ftrace_read_timestamp(struct pstore_ftrace_record *rec)
{
return rec->ts;
}
static inline void
pstore_ftrace_write_timestamp(struct pstore_ftrace_record *rec, u64 val)
{
rec->ts = val;
}
#else
static inline void
pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
{
rec->ts &= ~(TS_CPU_MASK);
rec->ts |= cpu;
}
static inline unsigned int
pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
{
return rec->ts & TS_CPU_MASK;
}
static inline u64
pstore_ftrace_read_timestamp(struct pstore_ftrace_record *rec)
{
return rec->ts >> TS_CPU_SHIFT;
}
static inline void
pstore_ftrace_write_timestamp(struct pstore_ftrace_record *rec, u64 val)
{
rec->ts = (rec->ts & TS_CPU_MASK) | (val << TS_CPU_SHIFT);
}
#endif
#endif /*_LINUX_PSTORE_H*/ #endif /*_LINUX_PSTORE_H*/
...@@ -24,6 +24,13 @@ ...@@ -24,6 +24,13 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/types.h> #include <linux/types.h>
/*
* Choose whether access to the RAM zone requires locking or not. If a zone
* can be written to from different CPUs like with ftrace for example, then
* PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
*/
#define PRZ_FLAG_NO_LOCK BIT(0)
struct persistent_ram_buffer; struct persistent_ram_buffer;
struct rs_control; struct rs_control;
...@@ -40,6 +47,8 @@ struct persistent_ram_zone { ...@@ -40,6 +47,8 @@ struct persistent_ram_zone {
void *vaddr; void *vaddr;
struct persistent_ram_buffer *buffer; struct persistent_ram_buffer *buffer;
size_t buffer_size; size_t buffer_size;
u32 flags;
raw_spinlock_t buffer_lock;
/* ECC correction */ /* ECC correction */
char *par_buffer; char *par_buffer;
...@@ -55,7 +64,7 @@ struct persistent_ram_zone { ...@@ -55,7 +64,7 @@ struct persistent_ram_zone {
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info, u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype); unsigned int memtype, u32 flags);
void persistent_ram_free(struct persistent_ram_zone *prz); 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);
...@@ -77,6 +86,8 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, ...@@ -77,6 +86,8 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
* @mem_address physical memory address to contain ramoops * @mem_address physical memory address to contain ramoops
*/ */
#define RAMOOPS_FLAG_FTRACE_PER_CPU BIT(0)
struct ramoops_platform_data { struct ramoops_platform_data {
unsigned long mem_size; unsigned long mem_size;
phys_addr_t mem_address; phys_addr_t mem_address;
...@@ -86,6 +97,7 @@ struct ramoops_platform_data { ...@@ -86,6 +97,7 @@ struct ramoops_platform_data {
unsigned long ftrace_size; unsigned long ftrace_size;
unsigned long pmsg_size; unsigned long pmsg_size;
int dump_oops; int dump_oops;
u32 flags;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
}; };
......
...@@ -4258,6 +4258,23 @@ int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, ...@@ -4258,6 +4258,23 @@ int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
} }
EXPORT_SYMBOL_GPL(ftrace_set_filter_ip); EXPORT_SYMBOL_GPL(ftrace_set_filter_ip);
/**
* ftrace_ops_set_global_filter - setup ops to use global filters
* @ops - the ops which will use the global filters
*
* ftrace users who need global function trace filtering should call this.
* It can set the global filter only if ops were not initialized before.
*/
void ftrace_ops_set_global_filter(struct ftrace_ops *ops)
{
if (ops->flags & FTRACE_OPS_FL_INITIALIZED)
return;
ftrace_ops_init(ops);
ops->func_hash = &global_ops.local_hash;
}
EXPORT_SYMBOL_GPL(ftrace_ops_set_global_filter);
static int static int
ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
int reset, int enable) int reset, int enable)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment