From patchwork Mon Apr 4 18:19:34 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grant Erickson X-Patchwork-Id: 89683 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8272FB6EF1 for ; Tue, 5 Apr 2011 04:21:26 +1000 (EST) Received: from canuck.infradead.org ([2001:4978:20e::1]) by bombadil.infradead.org with esmtps (Exim 4.72 #1 (Red Hat Linux)) id 1Q6oNS-00017e-Mq; Mon, 04 Apr 2011 18:19:50 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1Q6oNR-0005YJ-15; Mon, 04 Apr 2011 18:19:49 +0000 Received: from mail-vw0-f49.google.com ([209.85.212.49]) by canuck.infradead.org with esmtps (Exim 4.72 #1 (Red Hat Linux)) id 1Q6oNN-0005Xj-Dv for linux-mtd@lists.infradead.org; Mon, 04 Apr 2011 18:19:46 +0000 Received: by vws8 with SMTP id 8so5289703vws.36 for ; Mon, 04 Apr 2011 11:19:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:subject:date:message-id:x-mailer :in-reply-to:references; bh=fPIBUoFhqFw1Lb2kBzP2s71AGQ0KVvZ92TDBSXMcW3E=; b=lEQvr9EC3Rkrvo6SUcWSU7Om++jn6Q1LkdP2GdAMVA3tvQx/GEEav2VF+DJrN/VRfh U68f3NyYWiH4Dqc0lHgtahjIEQW5oqoV/teToFD54wVsYB8CMu9mf9aHD1gjLvpITlQw kN2kJEAwHRcZYQ15H7ELnD9A7gNzCvZfQzPN4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:subject:date:message-id:x-mailer:in-reply-to:references; b=WQJoRHErudPaAbZL5wNpNzXuFG5wRjwH6ThytnCS3HB6Nc84s25t48ANSZtnjFYUyO dRDVS5rTKtKGCM/c6JcQhySgA5W4aFvQQ0msAmfF8kP44hvsAoNIlhym1EzaNkpxp6Do kFOB9WgEeOV9dEY2Yp8n8OJYbsxxQmaUEeHLA= Received: by 10.52.89.103 with SMTP id bn7mr589654vdb.125.1301941184687; Mon, 04 Apr 2011 11:19:44 -0700 (PDT) Received: from localhost.localdomain (208.74.181.34.static.etheric.net [208.74.181.34]) by mx.google.com with ESMTPS id n13sm1193160vcr.17.2011.04.04.11.19.43 (version=SSLv3 cipher=OTHER); Mon, 04 Apr 2011 11:19:44 -0700 (PDT) From: Grant Erickson To: linux-mtd@lists.infradead.org Subject: [PATCH v2] MTD: Retry Read/Write Transfer Buffer Allocations Date: Mon, 4 Apr 2011 11:19:34 -0700 Message-Id: <1301941174-10050-1-git-send-email-marathon96@gmail.com> X-Mailer: git-send-email 1.7.4.2 In-Reply-To: <1301705049-15593-1-git-send-email-marathon96@gmail.com> References: <1301705049-15593-1-git-send-email-marathon96@gmail.com> X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110404_141945_644436_66F38E3E X-CRM114-Status: GOOD ( 18.65 ) X-Spam-Score: 1.4 (+) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (1.4 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.49 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is freemail (marathon96[at]gmail.com) 2.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends in digit (marathon96[at]gmail.com) -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.0 T_TO_NO_BRKTS_FREEMAIL T_TO_NO_BRKTS_FREEMAIL X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org When handling user space read or write requests via mtd_{read,write}, exponentially back off on the size of the requested kernel transfer buffer until it succeeds or until the requested transfer buffer size falls below the page size. This helps ensure the operation can succeed under low-memory, highly-fragmented situations albeit somewhat more slowly. v2: Added __GFP_NOWARN flag and made common retry loop a function as recommended by Artem. Signed-off-by: Grant Erickson --- drivers/mtd/mtdchar.c | 66 +++++++++++++++++++++++++++++++++--------------- 1 files changed, 45 insertions(+), 21 deletions(-) 1.7.4.2 diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 145b3d0d..df9be51 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -166,11 +166,44 @@ static int mtd_close(struct inode *inode, struct file *file) return 0; } /* mtd_close */ -/* FIXME: This _really_ needs to die. In 2.5, we should lock the - userspace buffer down and use it directly with readv/writev. -*/ +/* Back in April 2005, Linus wrote: + * + * FIXME: This _really_ needs to die. In 2.5, we should lock the + * userspace buffer down and use it directly with readv/writev. + * + * The implementation below, using mtd_try_alloc, mitigates allocation + * failures when the sytem is under low-memory situations or if memory + * is highly fragmented at the cost of reducing the performance of the + * requested transfer due to a smaller buffer size. + * + * A more complex but more memory-efficient implementation based on + * get_user_pages and iovecs to cover extents of those pages is a + * longer-term goal, as intimated by Linus above. However, for the + * write case, this requires yet more complex head and tail transfer + * handling when those head and tail offsets and sizes are such that + * alignment requirements are not met in the NAND subdriver. + */ #define MAX_KMALLOC_SIZE 0x20000 +static void *mtd_try_alloc(size_t *size) +{ + const gfp_t flags = (GFP_KERNEL | __GFP_NOWARN); + size_t try; + void *kbuf; + + try = min_t(size_t, *size, MAX_KMALLOC_SIZE); + + do { + kbuf = kmalloc(try, flags); + } while (!kbuf && ((try >>= 1) >= PAGE_SIZE)); + + if (kbuf) { + *size = try; + } + + return kbuf; +} + static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { struct mtd_file_info *mfi = file->private_data; @@ -179,6 +212,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t size_t total_retlen=0; int ret=0; int len; + size_t size; char *kbuf; DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); @@ -189,23 +223,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t if (!count) return 0; - /* FIXME: Use kiovec in 2.5 to lock down the user's buffers - and pass them directly to the MTD functions */ + size = count; - if (count > MAX_KMALLOC_SIZE) - kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); - else - kbuf=kmalloc(count, GFP_KERNEL); + kbuf = mtd_try_alloc(&size); if (!kbuf) return -ENOMEM; while (count) { - if (count > MAX_KMALLOC_SIZE) - len = MAX_KMALLOC_SIZE; - else - len = count; + len = min_t(size_t, count, size); switch (mfi->mode) { case MTD_MODE_OTP_FACTORY: @@ -268,6 +295,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; + size_t size; char *kbuf; size_t retlen; size_t total_retlen=0; @@ -285,21 +313,16 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count if (!count) return 0; - if (count > MAX_KMALLOC_SIZE) - kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); - else - kbuf=kmalloc(count, GFP_KERNEL); + size = count; + + kbuf = mtd_try_alloc(&size); if (!kbuf) return -ENOMEM; while (count) { - if (count > MAX_KMALLOC_SIZE) - len = MAX_KMALLOC_SIZE; - else - len = count; + len = min_t(size_t, count, size); if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); --