Hello all,
Currently, reading from flash is done one page at a time.
In this patch we implement the readpages routine, which
reads many data pages at once. Reading multiple flash pages
in one go reduces the device interaction which improves
read performance.
If a file is being read sequentially, we get requests of
upto 128KB in YAFFS2. If the data is sequential on flash,
we can issue 128KB read directly to flash.
OneNAND flash can read multiple flash pages faster.
So, there is significant improvement in read speed
with this feature.
Signed-off-by: Rohit Hagargundgi <
h.rohit@samsung.com>
---
diff --git a/fs/yaffs2/yaffs_fs.c b/fs/yaffs2/yaffs_fs.c
index 0c09033..3bfff57 100644
--- a/fs/yaffs2/yaffs_fs.c
+++ b/fs/yaffs2/yaffs_fs.c
@@ -51,6 +51,8 @@ extern const char *yaffs_guts_c_version;
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/ctype.h>
+#include <linux/pagevec.h>
+#include <linux/mm.h>
#include "asm/div64.h"
@@ -210,6 +212,8 @@ static void yaffs_delete_inode(struct inode *);
static void yaffs_clear_inode(struct inode *);
static int yaffs_readpage(struct file *file, struct page *page);
+static int yaffs_readpages(struct file *file, struct address_space
*mapping,
+ struct list_head *pageList, unsigned nrPages);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
static int yaffs_writepage(struct page *page, struct writeback_control
*wbc);
#else
@@ -229,6 +233,7 @@ static int yaffs_follow_link(struct dentry *dentry,
struct nameidata *nd);
#endif
static struct address_space_operations yaffs_file_address_operations = {
+ .readpages = yaffs_readpages,
.readpage = yaffs_readpage,
.writepage = yaffs_writepage,
.prepare_write = yaffs_prepare_write,
@@ -622,6 +627,96 @@ static int yaffs_readpage(struct file *f, struct page
*pg)
return yaffs_readpage_unlock(f, pg);
}
+static int yaffs_readpages(struct file *file, struct address_space
*mapping,
+ struct list_head *pages, unsigned nr_pages)
+{
+ struct pagevec lru_pvec;
+ struct page *page;
+ loff_t offset;
+ unsigned int readSize, i;
+ int readCnt = 0, pageaddfailed = 0;
+ unsigned contigPages;
+ unsigned long expectedIndex;
+
+ yaffs_Object *obj;
+ yaffs_Device *dev;
+ char *tmpBuf, *data;
+ unsigned maxPages = YAFFS_MAX_READAHEAD * 1024 / PAGE_CACHE_SIZE;
+
+ obj = yaffs_DentryToObject(file->f_dentry);
+ dev = obj->myDev;
+
+ pagevec_init(&lru_pvec, 0);
+ yaffs_GrossLock(dev);
+ tmpBuf = dev->readaheadBuffer;
+
+ for (i = 0; i < nr_pages; i += contigPages) {
+ if (list_empty(pages))
+ break;
+ expectedIndex = (loff_t)(list_entry(pages->prev,
+ struct page,
lru))->index;
+ offset = expectedIndex << PAGE_CACHE_SHIFT;
+ contigPages = 0;
+ list_for_each_entry_reverse(page, pages, lru) {
+ if (page->index != expectedIndex)
+ break;
+ if (add_to_page_cache(page, mapping,
+ page->index, GFP_KERNEL)) {
+ printk(KERN_CRIT "Add page cache failed\n");
+ pageaddfailed = 1;
+ break;
+ }
+ BUG_ON(!PageLocked(page));
+ contigPages++;
+ expectedIndex++;
+ }
+ if (contigPages + i > nr_pages)
+ contigPages = nr_pages - i;
+ if (contigPages > maxPages)
+ contigPages = maxPages;
+ readSize = contigPages * PAGE_CACHE_SIZE;
+ readCnt =
+ yaffs_ReadDataFromFile(obj, tmpBuf, offset, readSize)
+ & PAGE_CACHE_MASK;
+ data = tmpBuf;
+ while (readSize) {
+ page = list_entry(pages->prev, struct page, lru);
+ list_del(&page->lru);
+
+ if (readCnt > 0) {
+ memcpy(kmap(page), data, PAGE_CACHE_SIZE);
+ kunmap(page);
+ data += PAGE_CACHE_SIZE;
+ SetPageUptodate(page);
+ ClearPageError(page);
+ readCnt -= PAGE_CACHE_SIZE;
+ } else {
+ ClearPageUptodate(page);
+ SetPageError(page);
+ }
+ flush_dcache_page(page);
+ UnlockPage(page);
+ if (!pagevec_add(&lru_pvec, page))
+ __pagevec_lru_add(&lru_pvec);
+
+ readSize -= PAGE_CACHE_SIZE;
+ }
+
+ if (pageaddfailed) {
+ page = list_entry(pages->prev, struct page, lru);
+ list_del(&page->lru);
+ page_cache_release(page);
+ i++;
+ pageaddfailed = 0;
+ }
+
+ }
+
+ yaffs_GrossUnlock(dev);
+ pagevec_lru_add(&lru_pvec);
+ return readCnt;
+}
+
/* writepage inspired by/stolen from smbfs */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
@@ -1609,10 +1704,11 @@ static void yaffs_put_super(struct super_block *sb)
/* we assume this is protected by lock_kernel() in mount/umount */
ylist_del(&dev->devList);
- if(dev->spareBuffer){
- YFREE(dev->spareBuffer);
- dev->spareBuffer = NULL;
- }
+ YFREE(dev->extendedSpareBuffer);
+
+ YFREE(dev->readaheadBuffer);
+
+ YFREE(dev->spareBuffer);
kfree(dev);
}
@@ -1888,11 +1984,43 @@ static struct super_block
*yaffs_internal_read_super(int yaffsVersion,
if (yaffsVersion == 2) {
dev->writeChunkWithTagsToNAND =
nandmtd2_WriteChunkWithTagsToNAND;
+ dev->readChunksWithTagsFromNAND =
+ nandmtd2_ReadChunksWithTagsFromNAND;
dev->readChunkWithTagsFromNAND =
nandmtd2_ReadChunkWithTagsFromNAND;
dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
- dev->spareBuffer = YMALLOC(mtd->oobsize);
+ dev->spareBuffer = YMALLOC(mtd->oobavail *
+ ((YAFFS_MAX_READAHEAD * 1024)
+ / mtd->writesize));
+ if (!dev->spareBuffer) {
+ T(YAFFS_TRACE_ALWAYS,
+ ("yaffs_read_super: Failed allocating"
+ "spareBuffer.\n"));
+ YFREE(dev);
+ return NULL;
+ }
+ dev->readaheadBuffer = YMALLOC(YAFFS_MAX_READAHEAD * 1024);
+ if (!dev->readaheadBuffer) {
+ T(YAFFS_TRACE_ALWAYS,
+ ("yaffs_read_super: Failed allocating"
+ "readaheadBuffer.\n"));
+ YFREE(dev->spareBuffer);
+ YFREE(dev);
+ return NULL;
+ }
+ dev->extendedSpareBuffer =
YMALLOC(sizeof(yaffs_ExtendedTags) *
+ ((YAFFS_MAX_READAHEAD * 1024)
+ / mtd->writesize));
+ if (!dev->extendedSpareBuffer) {
+ T(YAFFS_TRACE_ALWAYS,
+ ("yaffs_read_super: Failed allocating"
+ "extendedSpareBuffer.\n"));
+ YFREE(dev->readaheadBuffer);
+ YFREE(dev->spareBuffer);
+ YFREE(dev);
+ return NULL;
+ }
dev->isYaffs2 = 1;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
dev->totalBytesPerChunk = mtd->writesize;
diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c
index 277906e..38ade0e 100644
--- a/fs/yaffs2/yaffs_guts.c
+++ b/fs/yaffs2/yaffs_guts.c
@@ -3517,6 +3520,20 @@ static int yaffs_PutChunkIntoFile(yaffs_Object * in,
int chunkInInode,
return YAFFS_OK;
}
+static int yaffs_ReadChunksDataFromObject(yaffs_Object *in, int
chunkInInode,
+ int nChunks, __u8 *buffer)
+{
+ int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL);
+ if (chunkInNAND >= 0)
+ return yaffs_ReadChunksWithTagsFromNAND(in->myDev,
chunkInNAND,
+ nChunks, buffer,
NULL);
+ T(YAFFS_TRACE_NANDACCESS,
+ (TSTR("Chunk %d not found zero instead"TENDSTR),
+ chunkInNAND));
+ memset(buffer, 0, in->myDev->nDataBytesPerChunk * nChunks);
+ return nChunks;
+}
+
static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int
chunkInInode,
__u8 * buffer)
{
@@ -4770,8 +4787,28 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 *
buffer, loff_t offset,
#endif
#else
- /* A full chunk. Read directly into the supplied
buffer. */
- yaffs_ReadChunkDataFromObject(in, chunk, buffer);
+ int nChunks = 1, thisChunkInNAND;
+ int nextChunkInNAND, nChunksRead;
+
+ thisChunkInNAND =
+ yaffs_FindChunkInFile(in, chunk,
NULL);
+
+ while (n - nToCopy >= dev->nDataBytesPerChunk) {
+ nextChunkInNAND =
+ yaffs_FindChunkInFile(in, chunk + nChunks,
NULL);
+ if (nextChunkInNAND != thisChunkInNAND + 1)
+ break;
+ nToCopy += dev->nDataBytesPerChunk;
+ nChunks++;
+ thisChunkInNAND++;
+ }
+
+ nChunksRead = yaffs_ReadChunksDataFromObject(in,
chunk,
+ nChunks , buffer);
+ if (nChunks != nChunksRead) {
+ nDone += nChunksRead *
dev->nDataBytesPerChunk;
+ break;
+ }
#endif
}
diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h
index a193625..dde1d89 100644
--- a/fs/yaffs2/yaffs_guts.h
+++ b/fs/yaffs2/yaffs_guts.h
@@ -90,6 +90,11 @@
#define YAFFS_MAX_SHORT_OP_CACHES 20
+/* Max number of kilobytes that we can read
+ * at one go
+ */
+#define YAFFS_MAX_READAHEAD VM_MAX_READAHEAD
+
#define YAFFS_N_TEMP_BUFFERS 6
/* We limit the number attempts at sucessfully saving a chunk of data.
@@ -583,6 +588,9 @@ struct yaffs_DeviceStruct {
int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev,
int chunkInNAND, __u8 * data,
yaffs_ExtendedTags * tags);
+ int (*readChunksWithTagsFromNAND) (struct yaffs_DeviceStruct *dev,
+ int chunkInNAND, int nChunks,
+ __u8 *data, yaffs_ExtendedTags
*tags);
int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int
blockNo);
int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo,
yaffs_BlockState * state, __u32
*sequenceNumber);
@@ -635,6 +643,8 @@ struct yaffs_DeviceStruct {
__u8 *spareBuffer; /* For mtdif2 use. Don't know the size of
the buffer
* at compile time so we have to allocate
it.
*/
+ yaffs_ExtendedTags *extendedSpareBuffer;
+ __u8 *readaheadBuffer;
void (*putSuperFunc) (struct super_block * sb);
#endif
diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c
index d007096..90295df 100644
--- a/fs/yaffs2/yaffs_mtdif2.c
+++ b/fs/yaffs2/yaffs_mtdif2.c
@@ -96,6 +96,70 @@ int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev,
int chunkInNAND,
return YAFFS_FAIL;
}
+int nandmtd2_ReadChunksWithTagsFromNAND(yaffs_Device *dev , int chunkInNAND
,
+ int nChunks , __u8 *data , yaffs_ExtendedTags *tags)
+{
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
+ struct mtd_oob_ops ops;
+#endif
+ size_t nRead = 0, nSprRead = 0;
+ unsigned nChunksRead;
+ int retval = 0;
+ int nDataBytes = data ? dev->nDataBytesPerChunk * nChunks : 0;
+ int nTagBytes = tags ? mtd->oobavail * nChunks : 0;
+ int i;
+ __u8 *spareBuffer = NULL, *tmp;
+ loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
+ T(YAFFS_TRACE_MTD,
+ (TSTR
+ ("nandmtd2_ReadChunksWithTagsFromNAND chunk %d no %d data %p tags
%p"
+ TENDSTR), chunkInNAND, nChunks, data, tags));
+
+ if (data && !tags)
+ retval = mtd->read(mtd, addr, nDataBytes, &nRead, data);
+ else if (tags) {
+ spareBuffer = dev->spareBuffer;
+ memset(spareBuffer, 0, nTagBytes);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooblen = nTagBytes;
+ ops.len = nDataBytes;
+ ops.ooboffs = 0;
+ ops.datbuf = data;
+ ops.oobbuf = spareBuffer;
+ retval = mtd->read_oob(mtd, addr, &ops);
+ nSprRead = ops.oobretlen;
+ nRead = data ? ops.retlen : 0;
+#else
+ if (data) {
+ retval = mtd->read_ecc(mtd, addr, nDataBytes,
&nRead,
+ data, spareBuffer, NULL);
+ }
+ if (tags) {
+ retval = mtd->read_oob(mtd, addr, nTagBytes,
+ &nSprRead, spareBuffer);
+ }
+#endif
+ }
+ nChunksRead = data ? nRead / dev->nDataBytesPerChunk :
+ nSprRead / mtd->oobavail;
+ if (tags) {
+ tmp = spareBuffer;
+ for (i = 0; i < nChunksRead; i++) {
+ yaffs_UnpackTags2(tags, (yaffs_PackedTags2 *)tmp);
+ if (retval == -EBADMSG &&
+ tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
+ if (i == nChunksRead - 1)
+ tags->eccResult =
+ YAFFS_ECC_RESULT_UNFIXED;
+ tmp += mtd->oobavail;
+ tags++;
+ }
+ }
+ return nChunksRead;
+}
+
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * data, yaffs_ExtendedTags *
tags)
{
diff --git a/fs/yaffs2/yaffs_mtdif2.h b/fs/yaffs2/yaffs_mtdif2.h
index 19f63b3..7aca137 100644
--- a/fs/yaffs2/yaffs_mtdif2.h
+++ b/fs/yaffs2/yaffs_mtdif2.h
@@ -20,6 +20,8 @@
int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags * tags);
+int nandmtd2_ReadChunksWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND,
+ int nChunks, __u8 *data, yaffs_ExtendedTags *tags);
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * data, yaffs_ExtendedTags *
tags);
int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c
index 932b2b5..cb0aba7 100644
--- a/fs/yaffs2/yaffs_nand.c
+++ b/fs/yaffs2/yaffs_nand.c
@@ -20,6 +20,33 @@ const char *yaffs_nand_c_version =
#include "yaffs_getblockinfo.h"
+int yaffs_ReadChunksWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND,
+ int nChunks, __u8 *buffer,
+ yaffs_ExtendedTags *tags)
+{
+ int nChunksRead = 0;
+ int realignedChunkInNAND = chunkInNAND - dev->chunkOffset;
+ int nTagBytes = sizeof(yaffs_ExtendedTags) * nChunks;
+ int i;
+ yaffs_ExtendedTags *tmp;
+
+ if (!tags)
+ tags = dev->extendedSpareBuffer;
+ memset(tags, 0, nTagBytes);
+
+ nChunksRead = dev->readChunksWithTagsFromNAND(dev,
realignedChunkInNAND,
+ nChunks, buffer, tags);
+ tmp = tags;
+ for (i = 0; i < nChunksRead; i++, tmp++) {
+ if (tmp->eccResult > YAFFS_ECC_RESULT_NO_ERROR) {
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,
+ (chunkInNAND + i)/dev->nChunksPerBlock);
+ yaffs_HandleChunkError(dev, bi);
+ }
+ }
+ return nChunksRead;
+}
+
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * buffer,
yaffs_ExtendedTags * tags)
diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h
index 5fa334b..6ea665a 100644
--- a/fs/yaffs2/yaffs_nand.h
+++ b/fs/yaffs2/yaffs_nand.h
@@ -19,6 +19,10 @@
+int yaffs_ReadChunksWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND,
+ int nChunks, __u8 *buffer,
+ yaffs_ExtendedTags *tags);
+
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * buffer,
yaffs_ExtendedTags * tags);