aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrant Erickson <marathon96@gmail.com>2011-04-08 11:51:32 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2011-05-24 20:59:43 -0400
commit33b53716bc4b3ff3da2bc41581226424443f9d5a (patch)
treea5127c750fe3297365dad1f5e3881666f363e11c
parent431e1ecabddcd7cbba237182ddf431771f98bb4c (diff)
mtd: create function to perform large allocations
Introduce a common function to handle large, contiguous kmalloc buffer allocations by exponentially backing 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. Artem: so this patch solves the problem that the kernel tries to kmalloc too large buffers, which (a) may fail and does fail - people complain about this, and (b) slows down the system in case of high memory fragmentation, because the kernel starts dropping caches, writing back, swapping, etc. But we do not really have to allocate a lot of memory to do the I/O, we may do this even with as little as one min. I/O unit (NAND page) of RAM. So the idea of this patch is that if the user asks to read or write a lot, we try to kmalloc a lot, with GFP flags which make the kernel _not_ drop caches, etc. If we can allocate it - good, if not - we try to allocate twice as less, and so on, until we reach the min. I/O unit size, which is our last resort allocation and use the normal GFP_KERNEL flag. Artem: re-write the allocation function so that it makes sure the allocated buffer is aligned to the min. I/O size of the flash. 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>
-rw-r--r--drivers/mtd/mtdcore.c49
-rw-r--r--include/linux/mtd/mtd.h2
2 files changed, 51 insertions, 0 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index da69bc8a5a7d..a50348b60d79 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -638,6 +638,54 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
638 return ret; 638 return ret;
639} 639}
640 640
641/**
642 * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size
643 * @size: A pointer to the ideal or maximum size of the allocation. Points
644 * to the actual allocation size on success.
645 *
646 * This routine attempts to allocate a contiguous kernel buffer up to
647 * the specified size, backing off the size of the request exponentially
648 * until the request succeeds or until the allocation size falls below
649 * the system page size. This attempts to make sure it does not adversely
650 * impact system performance, so when allocating more than one page, we
651 * ask the memory allocator to avoid re-trying, swapping, writing back
652 * or performing I/O.
653 *
654 * Note, this function also makes sure that the allocated buffer is aligned to
655 * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value.
656 *
657 * This is called, for example by mtd_{read,write} and jffs2_scan_medium,
658 * to handle smaller (i.e. degraded) buffer allocations under low- or
659 * fragmented-memory situations where such reduced allocations, from a
660 * requested ideal, are allowed.
661 *
662 * Returns a pointer to the allocated buffer on success; otherwise, NULL.
663 */
664void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size)
665{
666 gfp_t flags = __GFP_NOWARN | __GFP_WAIT |
667 __GFP_NORETRY | __GFP_NO_KSWAPD;
668 size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE);
669 void *kbuf;
670
671 *size = min_t(size_t, *size, KMALLOC_MAX_SIZE);
672
673 while (*size > min_alloc) {
674 kbuf = kmalloc(*size, flags);
675 if (kbuf)
676 return kbuf;
677
678 *size >>= 1;
679 *size = ALIGN(*size, mtd->writesize);
680 }
681
682 /*
683 * For the last resort allocation allow 'kmalloc()' to do all sorts of
684 * things (write-back, dropping caches, etc) by using GFP_KERNEL.
685 */
686 return kmalloc(*size, GFP_KERNEL);
687}
688
641EXPORT_SYMBOL_GPL(add_mtd_device); 689EXPORT_SYMBOL_GPL(add_mtd_device);
642EXPORT_SYMBOL_GPL(del_mtd_device); 690EXPORT_SYMBOL_GPL(del_mtd_device);
643EXPORT_SYMBOL_GPL(get_mtd_device); 691EXPORT_SYMBOL_GPL(get_mtd_device);
@@ -648,6 +696,7 @@ EXPORT_SYMBOL_GPL(__put_mtd_device);
648EXPORT_SYMBOL_GPL(register_mtd_user); 696EXPORT_SYMBOL_GPL(register_mtd_user);
649EXPORT_SYMBOL_GPL(unregister_mtd_user); 697EXPORT_SYMBOL_GPL(unregister_mtd_user);
650EXPORT_SYMBOL_GPL(default_mtd_writev); 698EXPORT_SYMBOL_GPL(default_mtd_writev);
699EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to);
651 700
652#ifdef CONFIG_PROC_FS 701#ifdef CONFIG_PROC_FS
653 702
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 9d5306bad117..06b489a7605b 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -348,6 +348,8 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
348int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, 348int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
349 unsigned long count, loff_t from, size_t *retlen); 349 unsigned long count, loff_t from, size_t *retlen);
350 350
351void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size);
352
351#ifdef CONFIG_MTD_PARTITIONS 353#ifdef CONFIG_MTD_PARTITIONS
352void mtd_erase_callback(struct erase_info *instr); 354void mtd_erase_callback(struct erase_info *instr);
353#else 355#else