Skip to content

Commit

Permalink
mtd: mtdchar: retry large buffer allocations
Browse files Browse the repository at this point in the history
Replace direct call to kmalloc for a potentially large, contiguous
buffer allocation with one to mtd_kmalloc_up_to which helps ensure the
operation can succeed under low-memory, highly- fragmented situations
albeit somewhat more slowly.

Signed-off-by: Grant Erickson <marathon96@gmail.com>
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
Tested-by: Stefano Babic <sbabic@denx.de>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
Grant Erickson authored and David Woodhouse committed May 25, 2011
1 parent 33b5371 commit 3e45cf5
Showing 1 changed file with 23 additions and 27 deletions.
50 changes: 23 additions & 27 deletions drivers/mtd/mtdchar.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,23 @@ 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.
*/
#define MAX_KMALLOC_SIZE 0x20000
/* Back in June 2001, dwmw2 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_kmalloc_up_to, mitigates
* allocation failures when the system 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 dwmw2 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.
*/

static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{
Expand All @@ -179,6 +192,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 = count;
char *kbuf;

DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
Expand All @@ -189,23 +203,12 @@ 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 */

if (count > MAX_KMALLOC_SIZE)
kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
else
kbuf=kmalloc(count, GFP_KERNEL);

kbuf = mtd_kmalloc_up_to(mtd, &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:
Expand Down Expand Up @@ -268,6 +271,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 = count;
char *kbuf;
size_t retlen;
size_t total_retlen=0;
Expand All @@ -285,20 +289,12 @@ 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);

kbuf = mtd_kmalloc_up_to(mtd, &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);
Expand Down

0 comments on commit 3e45cf5

Please sign in to comment.