[PATCH] mtd: Use fallback in memory allocation for mtd_{read,write}
Jarkko Lavinen
jarkko.lavinen at nokia.com
Tue Mar 22 08:13:29 EDT 2011
Kmalloc used in mtd_read() and mtd_write() can fail if the request
size is large and memory is fragmented. Use fall-back mechanism which
will quietly retry the allocation by halving the allocation size in each
retry.
Signed-off-by: Jarkko Lavinen <jarkko.lavinen at nokia.com>
---
drivers/mtd/mtdchar.c | 44 ++++++++++++++++++++++++++++++--------------
1 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 145b3d0d..18263d8 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -171,12 +171,33 @@ static int mtd_close(struct inode *inode, struct file *file)
*/
#define MAX_KMALLOC_SIZE 0x20000
+static void *mtd_buf_alloc(size_t *size)
+{
+ void *kbuf;
+ size_t next;
+
+ if (*size > MAX_KMALLOC_SIZE)
+ *size = MAX_KMALLOC_SIZE;
+
+ kbuf = kmalloc(*size, GFP_KERNEL | __GFP_NOWARN);
+ next = 1 << (fls(*size - 1) - 1);
+
+ while (!kbuf && next >= PAGE_SIZE) {
+ *size = next;
+ next /= 2;
+ kbuf = kmalloc(*size, GFP_KERNEL | __GFP_NOWARN);
+ }
+
+ 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;
struct mtd_info *mtd = mfi->mtd;
size_t retlen=0;
size_t total_retlen=0;
+ size_t alloc;
int ret=0;
int len;
char *kbuf;
@@ -192,18 +213,14 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
and pass them directly to the MTD functions */
- if (count > MAX_KMALLOC_SIZE)
- kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
- else
- kbuf=kmalloc(count, GFP_KERNEL);
-
+ alloc = count;
+ kbuf = mtd_buf_alloc(&alloc);
if (!kbuf)
return -ENOMEM;
while (count) {
-
- if (count > MAX_KMALLOC_SIZE)
- len = MAX_KMALLOC_SIZE;
+ if (count > alloc)
+ len = alloc;
else
len = count;
@@ -271,6 +288,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
char *kbuf;
size_t retlen;
size_t total_retlen=0;
+ size_t alloc;
int ret=0;
int len;
@@ -285,18 +303,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);
+ alloc = count;
+ kbuf = mtd_buf_alloc(&alloc);
if (!kbuf)
return -ENOMEM;
while (count) {
- if (count > MAX_KMALLOC_SIZE)
- len = MAX_KMALLOC_SIZE;
+ if (count > alloc)
+ len = alloc;
else
len = count;
--
1.7.2.5
More information about the linux-mtd
mailing list