mtd: mtdchar: retry large buffer allocations
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>
This commit is contained in:
Родитель
33b53716bc
Коммит
3e45cf5e85
|
@ -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)
|
||||
{
|
||||
|
@ -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");
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче