Commit a2d77302 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'pstore-efi' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6

* 'pstore-efi' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6:
  efivars: Introduce PSTORE_EFI_ATTRIBUTES
  efivars: Use string functions in pstore_write
  efivars: introduce utf16_strncmp
  efivars: String functions
  efi: Add support for using efivars as a pstore backend
  pstore: Allow the user to explicitly choose a backend
  pstore: Make "part" unsigned
  pstore: Add extra context for writes and erases
  pstore: Extend API for more flexibility in new backends
parents 72f9adfd 7644c16c
......@@ -39,3 +39,9 @@ Description: Generic interface to platform dependent persistent storage.
multiple) files based on the record size of the underlying
persistent storage until at least this amount is reached.
Default is 10 Kbytes.
Pstore only supports one backend at a time. If multiple
backends are available, the preferred backend may be
set by passing the pstore.backend= argument to the kernel at
boot time.
......@@ -2153,6 +2153,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
[HW,MOUSE] Controls Logitech smartscroll autorepeat.
0 = disabled, 1 = enabled (default).
pstore.backend= Specify the name of the pstore backend to use
pt. [PARIDE]
See Documentation/blockdev/paride.txt.
......
......@@ -932,8 +932,11 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time);
static u64 erst_writer(enum pstore_type_id type, size_t size);
struct timespec *time, struct pstore_info *psi);
static u64 erst_writer(enum pstore_type_id type, unsigned int part,
size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id,
struct pstore_info *psi);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
......@@ -942,7 +945,7 @@ static struct pstore_info erst_info = {
.close = erst_close_pstore,
.read = erst_reader,
.write = erst_writer,
.erase = erst_clear
.erase = erst_clearer
};
#define CPER_CREATOR_PSTORE \
......@@ -983,7 +986,7 @@ static int erst_close_pstore(struct pstore_info *psi)
}
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time)
struct timespec *time, struct pstore_info *psi)
{
int rc;
ssize_t len = 0;
......@@ -1037,7 +1040,8 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
return (rc < 0) ? rc : (len - sizeof(*rcd));
}
static u64 erst_writer(enum pstore_type_id type, size_t size)
static u64 erst_writer(enum pstore_type_id type, unsigned int part,
size_t size, struct pstore_info *psi)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
......@@ -1080,6 +1084,12 @@ static u64 erst_writer(enum pstore_type_id type, size_t size)
return rcd->hdr.record_id;
}
static int erst_clearer(enum pstore_type_id type, u64 id,
struct pstore_info *psi)
{
return erst_clear(id);
}
static int __init erst_init(void)
{
int rc = 0;
......
......@@ -78,6 +78,7 @@
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/pstore.h>
#include <asm/uaccess.h>
......@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
MODULE_LICENSE("GPL");
MODULE_VERSION(EFIVARS_VERSION);
#define DUMP_NAME_LEN 52
/*
* The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much
......@@ -119,6 +122,10 @@ struct efivar_attribute {
ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
};
#define PSTORE_EFI_ATTRIBUTES \
(EFI_VARIABLE_NON_VOLATILE | \
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
EFI_VARIABLE_RUNTIME_ACCESS)
#define EFIVAR_ATTR(_name, _mode, _show, _store) \
struct efivar_attribute efivar_attr_##_name = { \
......@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,
/* Return the number of unicode characters in data */
static unsigned long
utf8_strlen(efi_char16_t *data, unsigned long maxlength)
utf16_strnlen(efi_char16_t *s, size_t maxlength)
{
unsigned long length = 0;
while (*data++ != 0 && length < maxlength)
while (*s++ != 0 && length < maxlength)
length++;
return length;
}
static unsigned long
utf16_strlen(efi_char16_t *s)
{
return utf16_strnlen(s, ~0UL);
}
/*
* Return the number of bytes is the length of this string
* Note: this is NOT the same as the number of unicode characters
*/
static inline unsigned long
utf8_strsize(efi_char16_t *data, unsigned long maxlength)
utf16_strsize(efi_char16_t *data, unsigned long maxlength)
{
return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
}
static inline int
utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
{
while (1) {
if (len == 0)
return 0;
if (*a < *b)
return -1;
if (*a > *b)
return 1;
if (*a == 0) /* implies *b == 0 */
return 0;
a++;
b++;
len--;
}
}
static efi_status_t
get_var_data(struct efivars *efivars, struct efi_variable *var)
get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
{
efi_status_t status;
spin_lock(&efivars->lock);
var->DataSize = 1024;
status = efivars->ops->get_variable(var->VariableName,
&var->VendorGuid,
&var->Attributes,
&var->DataSize,
var->Data);
return status;
}
static efi_status_t
get_var_data(struct efivars *efivars, struct efi_variable *var)
{
efi_status_t status;
spin_lock(&efivars->lock);
status = get_var_data_locked(efivars, var);
spin_unlock(&efivars->lock);
if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
status);
......@@ -387,12 +428,180 @@ static struct kobj_type efivar_ktype = {
.default_attrs = def_attrs,
};
static struct pstore_info efi_pstore_info;
static inline void
efivar_unregister(struct efivar_entry *var)
{
kobject_put(&var->kobj);
}
#ifdef CONFIG_PSTORE
static int efi_pstore_open(struct pstore_info *psi)
{
struct efivars *efivars = psi->data;
spin_lock(&efivars->lock);
efivars->walk_entry = list_first_entry(&efivars->list,
struct efivar_entry, list);
return 0;
}
static int efi_pstore_close(struct pstore_info *psi)
{
struct efivars *efivars = psi->data;
spin_unlock(&efivars->lock);
return 0;
}
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
struct timespec *timespec, struct pstore_info *psi)
{
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct efivars *efivars = psi->data;
char name[DUMP_NAME_LEN];
int i;
unsigned int part, size;
unsigned long time;
while (&efivars->walk_entry->list != &efivars->list) {
if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
vendor)) {
for (i = 0; i < DUMP_NAME_LEN; i++) {
name[i] = efivars->walk_entry->var.VariableName[i];
}
if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
*id = part;
timespec->tv_sec = time;
timespec->tv_nsec = 0;
get_var_data_locked(efivars, &efivars->walk_entry->var);
size = efivars->walk_entry->var.DataSize;
memcpy(psi->buf, efivars->walk_entry->var.Data, size);
efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
struct efivar_entry, list);
return size;
}
}
efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
struct efivar_entry, list);
}
return 0;
}
static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
size_t size, struct pstore_info *psi)
{
char name[DUMP_NAME_LEN];
char stub_name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN];
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct efivars *efivars = psi->data;
struct efivar_entry *entry, *found = NULL;
int i;
sprintf(stub_name, "dump-type%u-%u-", type, part);
sprintf(name, "%s%lu", stub_name, get_seconds());
spin_lock(&efivars->lock);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = stub_name[i];
/*
* Clean up any entries with the same name
*/
list_for_each_entry(entry, &efivars->list, list) {
get_var_data_locked(efivars, &entry->var);
if (efi_guidcmp(entry->var.VendorGuid, vendor))
continue;
if (utf16_strncmp(entry->var.VariableName, efi_name,
utf16_strlen(efi_name)))
continue;
/* Needs to be a prefix */
if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
continue;
/* found */
found = entry;
efivars->ops->set_variable(entry->var.VariableName,
&entry->var.VendorGuid,
PSTORE_EFI_ATTRIBUTES,
0, NULL);
}
if (found)
list_del(&found->list);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
size, psi->buf);
spin_unlock(&efivars->lock);
if (found)
efivar_unregister(found);
if (size)
efivar_create_sysfs_entry(efivars,
utf16_strsize(efi_name,
DUMP_NAME_LEN * 2),
efi_name, &vendor);
return part;
};
static int efi_pstore_erase(enum pstore_type_id type, u64 id,
struct pstore_info *psi)
{
efi_pstore_write(type, id, 0, psi);
return 0;
}
#else
static int efi_pstore_open(struct pstore_info *psi)
{
return 0;
}
static int efi_pstore_close(struct pstore_info *psi)
{
return 0;
}
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
struct timespec *time, struct pstore_info *psi)
{
return -1;
}
static u64 efi_pstore_write(enum pstore_type_id type, int part, size_t size,
struct pstore_info *psi)
{
return 0;
}
static int efi_pstore_erase(enum pstore_type_id type, u64 id,
struct pstore_info *psi)
{
return 0;
}
#endif
static struct pstore_info efi_pstore_info = {
.owner = THIS_MODULE,
.name = "efi",
.open = efi_pstore_open,
.close = efi_pstore_close,
.read = efi_pstore_read,
.write = efi_pstore_write,
.erase = efi_pstore_erase,
};
static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
......@@ -414,8 +623,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
strsize2 = utf8_strsize(new_var->VariableName, 1024);
strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
strsize2 = utf16_strsize(new_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
new_var->VariableName, strsize1) &&
......@@ -447,8 +656,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
/* Create the entry in sysfs. Locking is not required here */
status = efivar_create_sysfs_entry(efivars,
utf8_strsize(new_var->VariableName,
1024),
utf16_strsize(new_var->VariableName,
1024),
new_var->VariableName,
&new_var->VendorGuid);
if (status) {
......@@ -477,8 +686,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
strsize2 = utf8_strsize(del_var->VariableName, 1024);
strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
strsize2 = utf16_strsize(del_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
del_var->VariableName, strsize1) &&
......@@ -763,6 +972,16 @@ int register_efivars(struct efivars *efivars,
if (error)
unregister_efivars(efivars);
efivars->efi_pstore_info = efi_pstore_info;
efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
if (efivars->efi_pstore_info.buf) {
efivars->efi_pstore_info.bufsize = 1024;
efivars->efi_pstore_info.data = efivars;
mutex_init(&efivars->efi_pstore_info.buf_mutex);
pstore_register(&efivars->efi_pstore_info);
}
out:
kfree(variable_name);
......
......@@ -39,8 +39,9 @@
#define PSTORE_NAMELEN 64
struct pstore_private {
struct pstore_info *psi;
enum pstore_type_id type;
u64 id;
int (*erase)(u64);
ssize_t size;
char data[];
};
......@@ -73,7 +74,7 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
struct pstore_private *p = dentry->d_inode->i_private;
p->erase(p->id);
p->psi->erase(p->type, p->id, p->psi);
return simple_unlink(dir, dentry);
}
......@@ -175,8 +176,8 @@ int pstore_is_mounted(void)
* Set the mtime & ctime to the date that this record was originally stored.
*/
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
char *data, size_t size,
struct timespec time, int (*erase)(u64))
char *data, size_t size, struct timespec time,
struct pstore_info *psi)
{
struct dentry *root = pstore_sb->s_root;
struct dentry *dentry;
......@@ -192,8 +193,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
private = kmalloc(sizeof *private + size, GFP_KERNEL);
if (!private)
goto fail_alloc;
private->type = type;
private->id = id;
private->erase = erase;
private->psi = psi;
switch (type) {
case PSTORE_TYPE_DMESG:
......
......@@ -2,5 +2,5 @@ extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(void);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
char *data, size_t size,
struct timespec time, int (*erase)(u64));
struct timespec time, struct pstore_info *psi);
extern int pstore_is_mounted(void);
......@@ -37,6 +37,8 @@
static DEFINE_SPINLOCK(pstore_lock);
static struct pstore_info *psinfo;
static char *backend;
/* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240;
......@@ -67,7 +69,8 @@ static void pstore_dump(struct kmsg_dumper *dumper,
unsigned long size, total = 0;
char *dst, *why;
u64 id;
int hsize, part = 1;
int hsize;
unsigned int part = 1;
if (reason < ARRAY_SIZE(reason_str))
why = reason_str[reason];
......@@ -78,7 +81,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
oopscount++;
while (total < kmsg_bytes) {
dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part++);
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
size = psinfo->bufsize - hsize;
dst += hsize;
......@@ -94,14 +97,16 @@ static void pstore_dump(struct kmsg_dumper *dumper,
memcpy(dst, s1 + s1_start, l1_cpy);
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy);
id = psinfo->write(PSTORE_TYPE_DMESG, part,
hsize + l1_cpy + l2_cpy, psinfo);
if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
psinfo->buf, hsize + l1_cpy + l2_cpy,
CURRENT_TIME, psinfo->erase);
CURRENT_TIME, psinfo);
l1 -= l1_cpy;
l2 -= l2_cpy;
total += l1_cpy + l2_cpy;
part++;
}
mutex_unlock(&psinfo->buf_mutex);
}
......@@ -128,6 +133,12 @@ int pstore_register(struct pstore_info *psi)
spin_unlock(&pstore_lock);
return -EBUSY;
}
if (backend && strcmp(backend, psi->name)) {
spin_unlock(&pstore_lock);
return -EINVAL;
}
psinfo = psi;
spin_unlock(&pstore_lock);
......@@ -166,9 +177,9 @@ void pstore_get_records(void)
if (rc)
goto out;
while ((size = psi->read(&id, &type, &time)) > 0) {
while ((size = psi->read(&id, &type, &time, psi)) > 0) {
if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
time, psi->erase))
time, psi))
failed++;
}
psi->close(psi);
......@@ -196,12 +207,15 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size)
mutex_lock(&psinfo->buf_mutex);
memcpy(psinfo->buf, buf, size);
id = psinfo->write(type, size);
id = psinfo->write(type, 0, size, psinfo);
if (pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
size, CURRENT_TIME, psinfo->erase);
size, CURRENT_TIME, psinfo);
mutex_unlock(&psinfo->buf_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_write);
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");
......@@ -19,6 +19,7 @@
#include <linux/rtc.h>
#include <linux/ioport.h>
#include <linux/pfn.h>
#include <linux/pstore.h>
#include <asm/page.h>
#include <asm/system.h>
......@@ -232,6 +233,9 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
typedef struct {
efi_guid_t guid;
unsigned long table;
......@@ -458,6 +462,8 @@ struct efivars {
struct kset *kset;
struct bin_attribute *new_var, *del_var;
const struct efivar_operations *ops;
struct efivar_entry *walk_entry;
struct pstore_info efi_pstore_info;
};
int register_efivars(struct efivars *efivars,
......
......@@ -38,9 +38,12 @@ struct pstore_info {
int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
struct timespec *time);
u64 (*write)(enum pstore_type_id type, size_t size);
int (*erase)(u64 id);
struct timespec *time, struct pstore_info *psi);
u64 (*write)(enum pstore_type_id type, unsigned int part,
size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id,
struct pstore_info *psi);
void *data;
};
#ifdef CONFIG_PSTORE
......
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