diff options
author | Yunhong Jiang <yunhong.jiang@intel.com> | 2015-08-04 12:17:53 -0700 |
---|---|---|
committer | Yunhong Jiang <yunhong.jiang@intel.com> | 2015-08-04 15:44:42 -0700 |
commit | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (patch) | |
tree | 1c9cafbcd35f783a87880a10f85d1a060db1a563 /kernel/drivers/acpi/apei/erst-dbg.c | |
parent | 98260f3884f4a202f9ca5eabed40b1354c489b29 (diff) |
Add the rt linux 4.1.3-rt3 as base
Import the rt linux 4.1.3-rt3 as OPNFV kvm base.
It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and
the base is:
commit 0917f823c59692d751951bf5ea699a2d1e2f26a2
Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Sat Jul 25 12:13:34 2015 +0200
Prepare v4.1.3-rt3
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
We lose all the git history this way and it's not good. We
should apply another opnfv project repo in future.
Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Diffstat (limited to 'kernel/drivers/acpi/apei/erst-dbg.c')
-rw-r--r-- | kernel/drivers/acpi/apei/erst-dbg.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/kernel/drivers/acpi/apei/erst-dbg.c b/kernel/drivers/acpi/apei/erst-dbg.c new file mode 100644 index 000000000..04ab5c9d3 --- /dev/null +++ b/kernel/drivers/acpi/apei/erst-dbg.c @@ -0,0 +1,243 @@ +/* + * APEI Error Record Serialization Table debug support + * + * ERST is a way provided by APEI to save and retrieve hardware error + * information to and from a persistent store. This file provide the + * debugging/testing support for ERST kernel support and firmware + * implementation. + * + * Copyright 2010 Intel Corp. + * Author: Huang Ying <ying.huang@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <acpi/apei.h> +#include <linux/miscdevice.h> + +#include "apei-internal.h" + +#define ERST_DBG_PFX "ERST DBG: " + +#define ERST_DBG_RECORD_LEN_MAX 0x4000 + +static void *erst_dbg_buf; +static unsigned int erst_dbg_buf_len; + +/* Prevent erst_dbg_read/write from being invoked concurrently */ +static DEFINE_MUTEX(erst_dbg_mutex); + +static int erst_dbg_open(struct inode *inode, struct file *file) +{ + int rc, *pos; + + if (erst_disable) + return -ENODEV; + + pos = (int *)&file->private_data; + + rc = erst_get_record_id_begin(pos); + if (rc) + return rc; + + return nonseekable_open(inode, file); +} + +static int erst_dbg_release(struct inode *inode, struct file *file) +{ + erst_get_record_id_end(); + + return 0; +} + +static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int rc; + u64 record_id; + u32 record_count; + + switch (cmd) { + case APEI_ERST_CLEAR_RECORD: + rc = copy_from_user(&record_id, (void __user *)arg, + sizeof(record_id)); + if (rc) + return -EFAULT; + return erst_clear(record_id); + case APEI_ERST_GET_RECORD_COUNT: + rc = erst_get_record_count(); + if (rc < 0) + return rc; + record_count = rc; + rc = put_user(record_count, (u32 __user *)arg); + if (rc) + return rc; + return 0; + default: + return -ENOTTY; + } +} + +static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, + size_t usize, loff_t *off) +{ + int rc, *pos; + ssize_t len = 0; + u64 id; + + if (*off) + return -EINVAL; + + if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) + return -EINTR; + + pos = (int *)&filp->private_data; + +retry_next: + rc = erst_get_record_id_next(pos, &id); + if (rc) + goto out; + /* no more record */ + if (id == APEI_ERST_INVALID_RECORD_ID) { + /* + * If the persistent store is empty initially, the function + * 'erst_read' below will return "-ENOENT" value. This causes + * 'retry_next' label is entered again. The returned value + * should be zero indicating the read operation is EOF. + */ + len = 0; + + goto out; + } +retry: + rc = len = erst_read(id, erst_dbg_buf, erst_dbg_buf_len); + /* The record may be cleared by others, try read next record */ + if (rc == -ENOENT) + goto retry_next; + if (rc < 0) + goto out; + if (len > ERST_DBG_RECORD_LEN_MAX) { + pr_warning(ERST_DBG_PFX + "Record (ID: 0x%llx) length is too long: %zd\n", + id, len); + rc = -EIO; + goto out; + } + if (len > erst_dbg_buf_len) { + void *p; + rc = -ENOMEM; + p = kmalloc(len, GFP_KERNEL); + if (!p) + goto out; + kfree(erst_dbg_buf); + erst_dbg_buf = p; + erst_dbg_buf_len = len; + goto retry; + } + + rc = -EINVAL; + if (len > usize) + goto out; + + rc = -EFAULT; + if (copy_to_user(ubuf, erst_dbg_buf, len)) + goto out; + rc = 0; +out: + mutex_unlock(&erst_dbg_mutex); + return rc ? rc : len; +} + +static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf, + size_t usize, loff_t *off) +{ + int rc; + struct cper_record_header *rcd; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (usize > ERST_DBG_RECORD_LEN_MAX) { + pr_err(ERST_DBG_PFX "Too long record to be written\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&erst_dbg_mutex)) + return -EINTR; + if (usize > erst_dbg_buf_len) { + void *p; + rc = -ENOMEM; + p = kmalloc(usize, GFP_KERNEL); + if (!p) + goto out; + kfree(erst_dbg_buf); + erst_dbg_buf = p; + erst_dbg_buf_len = usize; + } + rc = copy_from_user(erst_dbg_buf, ubuf, usize); + if (rc) { + rc = -EFAULT; + goto out; + } + rcd = erst_dbg_buf; + rc = -EINVAL; + if (rcd->record_length != usize) + goto out; + + rc = erst_write(erst_dbg_buf); + +out: + mutex_unlock(&erst_dbg_mutex); + return rc < 0 ? rc : usize; +} + +static const struct file_operations erst_dbg_ops = { + .owner = THIS_MODULE, + .open = erst_dbg_open, + .release = erst_dbg_release, + .read = erst_dbg_read, + .write = erst_dbg_write, + .unlocked_ioctl = erst_dbg_ioctl, + .llseek = no_llseek, +}; + +static struct miscdevice erst_dbg_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "erst_dbg", + .fops = &erst_dbg_ops, +}; + +static __init int erst_dbg_init(void) +{ + if (erst_disable) { + pr_info(ERST_DBG_PFX "ERST support is disabled.\n"); + return -ENODEV; + } + return misc_register(&erst_dbg_dev); +} + +static __exit void erst_dbg_exit(void) +{ + misc_deregister(&erst_dbg_dev); + kfree(erst_dbg_buf); +} + +module_init(erst_dbg_init); +module_exit(erst_dbg_exit); + +MODULE_AUTHOR("Huang Ying"); +MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support"); +MODULE_LICENSE("GPL"); |