From patchwork Sun Jul 12 10:25:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Finn Thain X-Patchwork-Id: 494087 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 2B95F140216 for ; Sun, 12 Jul 2015 20:57:51 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 11D111A1735 for ; Sun, 12 Jul 2015 20:57:51 +1000 (AEST) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Received: from kvm5.telegraphics.com.au (kvm5.telegraphics.com.au [98.124.60.144]) by lists.ozlabs.org (Postfix) with ESMTP id E98061A0D0A for ; Sun, 12 Jul 2015 20:40:40 +1000 (AEST) Received: by kvm5.telegraphics.com.au (Postfix, from userid 502) id 4849727FF5; Sun, 12 Jul 2015 06:40:35 -0400 (EDT) Message-Id: <20150712102529.565913150@telegraphics.com.au> User-Agent: quilt/0.50-1 Date: Sun, 12 Jul 2015 20:25:36 +1000 From: Finn Thain To: , , , Arnd Bergmann , Greg Kroah-Hartman Subject: [RFC v4 09/25] char/nvram: Implement NVRAM read/write methods References: <20150712102527.356151908@telegraphics.com.au> Content-Disposition: inline; filename=nvram-generalize-read-write X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" Refactor the RTC "CMOS" NVRAM functions so that they can be used as arch_nvram_ops methods. Checksumming logic is moved from the misc device operations to the nvram read/write operations. This makes the misc device implementation more generic. This also preserves the locking semantics such that "read if checksum valid" and "write and update checksum" remain atomic operations. PPC64 implements byte-range read/write methods which are similar to file_operations struct methods. Other platforms provide only byte-at-a-time functions. So the misc device prefers the former but will fall back on the latter. Signed-off-by: Finn Thain --- drivers/char/nvram.c | 162 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 48 deletions(-) Index: linux/drivers/char/nvram.c =================================================================== --- linux.orig/drivers/char/nvram.c 2015-07-12 20:25:02.000000000 +1000 +++ linux/drivers/char/nvram.c 2015-07-12 20:25:03.000000000 +1000 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -178,9 +179,48 @@ static ssize_t nvram_get_size(void) return NVRAM_BYTES; } +static ssize_t nvram_read(char *buf, size_t count, loff_t *ppos) +{ + char *p = buf; + loff_t i; + + spin_lock_irq(&rtc_lock); + if (!__nvram_check_checksum()) { + spin_unlock_irq(&rtc_lock); + return -EIO; + } + for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) + *p = __nvram_read_byte(i); + spin_unlock_irq(&rtc_lock); + + *ppos = i; + return p - buf; +} + +static ssize_t nvram_write(char *buf, size_t count, loff_t *ppos) +{ + char *p = buf; + loff_t i; + + spin_lock_irq(&rtc_lock); + if (!__nvram_check_checksum()) { + spin_unlock_irq(&rtc_lock); + return -EIO; + } + for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) + __nvram_write_byte(*p, i); + __nvram_set_checksum(); + spin_unlock_irq(&rtc_lock); + + *ppos = i; + return p - buf; +} + const struct nvram_ops arch_nvram_ops = { .read_byte = nvram_read_byte, .write_byte = nvram_write_byte, + .read = nvram_read, + .write = nvram_write, .get_size = nvram_get_size, .set_checksum = nvram_set_checksum, .initialize = nvram_initialize, @@ -215,69 +255,95 @@ static loff_t nvram_misc_llseek(struct f static ssize_t nvram_misc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - unsigned char contents[NVRAM_BYTES]; - unsigned i = *ppos; - unsigned char *tmp; - - spin_lock_irq(&rtc_lock); + loff_t i; + char __user *p = buf; - if (!__nvram_check_checksum()) - goto checksum_err; - - for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp) - *tmp = __nvram_read_byte(i); - - spin_unlock_irq(&rtc_lock); - - if (copy_to_user(buf, contents, tmp - contents)) + if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; + if (*ppos >= nvram_size) + return 0; - *ppos = i; - - return tmp - contents; + /* If the arch provided a byte range read op, use it. Otherwise + * fall back on the byte-at-a-time accessor. + */ + if (arch_nvram_ops.read != NULL) { + char *tmp; + ssize_t ret; + + count = min_t(size_t, count, nvram_size - *ppos); + count = min_t(size_t, count, PAGE_SIZE); + + tmp = kmalloc(count, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = arch_nvram_ops.read(tmp, count, ppos); + if (ret <= 0) + goto out; + + if (copy_to_user(buf, tmp, ret)) { + *ppos -= ret; + ret = -EFAULT; + } + +out: + kfree(tmp); + return ret; + } -checksum_err: - spin_unlock_irq(&rtc_lock); - return -EIO; + for (i = *ppos; count > 0 && i < nvram_size; ++i, ++p, --count) + if (__put_user(arch_nvram_ops.read_byte(i), p)) + return -EFAULT; + *ppos = i; + return p - buf; } static ssize_t nvram_misc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - unsigned char contents[NVRAM_BYTES]; - unsigned i = *ppos; - unsigned char *tmp; - - if (i >= NVRAM_BYTES) - return 0; /* Past EOF */ - - if (count > NVRAM_BYTES - i) - count = NVRAM_BYTES - i; - if (count > NVRAM_BYTES) - return -EFAULT; /* Can't happen, but prove it to gcc */ + loff_t i; + const char __user *p = buf; - if (copy_from_user(contents, buf, count)) + if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; + if (*ppos >= nvram_size) + return 0; - spin_lock_irq(&rtc_lock); - - if (!__nvram_check_checksum()) - goto checksum_err; - - for (tmp = contents; count--; ++i, ++tmp) - __nvram_write_byte(*tmp, i); - - __nvram_set_checksum(); + /* If the arch provided a byte range write op, use it. Otherwise + * fall back on the byte-at-a-time accessor. + */ + if (arch_nvram_ops.write != NULL) { + char *tmp; + ssize_t ret; + + count = min_t(size_t, count, nvram_size - *ppos); + count = min_t(size_t, count, PAGE_SIZE); + + tmp = kmalloc(count, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + if (copy_from_user(tmp, buf, count)) { + ret = -EFAULT; + goto out; + } + + ret = arch_nvram_ops.write(tmp, count, ppos); + +out: + kfree(tmp); + return ret; + } - spin_unlock_irq(&rtc_lock); + for (i = *ppos; count > 0 && i < nvram_size; ++i, ++p, --count) { + char c; + if (__get_user(c, p)) + return -EFAULT; + arch_nvram_ops.write_byte(c, i); + } *ppos = i; - - return tmp - contents; - -checksum_err: - spin_unlock_irq(&rtc_lock); - return -EIO; + return p - buf; } static long nvram_misc_ioctl(struct file *file, unsigned int cmd,