From: Len Brown Date: Wed, 29 Sep 2010 19:18:28 +0000 (-0400) Subject: Merge branches 'apei', 'battery-mwh-fix', 'bugzilla-10807', 'bugzilla-14736', 'bugzil... X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=fdb8c58a1671beb51949412e053926acd5500b5f;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git Merge branches 'apei', 'battery-mwh-fix', 'bugzilla-10807', 'bugzilla-14736', 'bugzilla-14679', 'bugzilla-16396', 'launchpad-613381' and 'misc' into release --- fdb8c58a1671beb51949412e053926acd5500b5f diff --cc drivers/acpi/apei/erst-dbg.c index 8561cfefa3dc,000000000000,5281ddda2777,5281ddda2777,5281ddda2777,5281ddda2777,5281ddda2777,98ffa2991ebc..da1228a9a544 mode 100644,000000,100644,100644,100644,100644,100644,100644..100644 --- a/drivers/acpi/apei/erst-dbg.c +++ b/drivers/acpi/apei/erst-dbg.c @@@@@@@@@ -1,211 -1,0 -1,207 -1,207 -1,207 -1,207 -1,207 -1,207 +1,211 @@@@@@@@@ + /* + * APEI Error Record Serialization Table debug support + * + * ERST is a way provided by APEI to save and retrieve hardware error - ----- * infomation to and from a persistent store. This file provide the +++++++ * 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 + * + * 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 + #include + #include + #include + #include + + #include "apei-internal.h" + + #define ERST_DBG_PFX "ERST DBG: " + + #define ERST_DBG_RECORD_LEN_MAX 4096 + + 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) + { + if (erst_disable) + return -ENODEV; + + return nonseekable_open(inode, file); + } + + 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; + ssize_t len = 0; + u64 id; + + if (*off != 0) + return -EINVAL; + + if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) + return -EINTR; + + retry_next: + rc = erst_get_next_record_id(&id); + if (rc) + goto out; + /* no more record */ + if (id == APEI_ERST_INVALID_RECORD_ID) + 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) { ------ kfree(erst_dbg_buf); +++++++ void *p; + rc = -ENOMEM; ------ erst_dbg_buf = kmalloc(len, GFP_KERNEL); ------ if (!erst_dbg_buf) +++++++ 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) { ------ kfree(erst_dbg_buf); +++++++ void *p; + rc = -ENOMEM; ------ erst_dbg_buf = kmalloc(usize, GFP_KERNEL); ------ if (!erst_dbg_buf) +++++++ 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, + .read = erst_dbg_read, + .write = erst_dbg_write, + .unlocked_ioctl = erst_dbg_ioctl, + }; + + static struct miscdevice erst_dbg_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "erst_dbg", + .fops = &erst_dbg_ops, + }; + + static __init int erst_dbg_init(void) + { + 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");