summaryrefslogtreecommitdiff
path: root/fs/sdfat/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sdfat/cache.c')
-rw-r--r--fs/sdfat/cache.c846
1 files changed, 846 insertions, 0 deletions
diff --git a/fs/sdfat/cache.c b/fs/sdfat/cache.c
new file mode 100644
index 000000000000..8318898646be
--- /dev/null
+++ b/fs/sdfat/cache.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/************************************************************************/
+/* */
+/* PROJECT : exFAT & FAT12/16/32 File System */
+/* FILE : cache.c */
+/* PURPOSE : sdFAT Cache Manager */
+/* (FAT Cache & Buffer Cache) */
+/* */
+/*----------------------------------------------------------------------*/
+/* NOTES */
+/* */
+/* */
+/************************************************************************/
+
+#include <linux/swap.h> /* for mark_page_accessed() */
+#include <asm/unaligned.h>
+
+#include "sdfat.h"
+#include "core.h"
+
+#define DEBUG_HASH_LIST
+#define DEBUG_HASH_PREV (0xAAAA5555)
+#define DEBUG_HASH_NEXT (0x5555AAAA)
+
+/*----------------------------------------------------------------------*/
+/* Global Variable Definitions */
+/*----------------------------------------------------------------------*/
+/* All buffer structures are protected w/ fsi->v_sem */
+
+/*----------------------------------------------------------------------*/
+/* Local Variable Definitions */
+/*----------------------------------------------------------------------*/
+#define LOCKBIT (0x01)
+#define DIRTYBIT (0x02)
+#define KEEPBIT (0x04)
+
+/*----------------------------------------------------------------------*/
+/* Cache handling function declarations */
+/*----------------------------------------------------------------------*/
+static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec);
+static cache_ent_t *__fcache_get(struct super_block *sb);
+static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp);
+static void __fcache_remove_hash(cache_ent_t *bp);
+
+static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec);
+static cache_ent_t *__dcache_get(struct super_block *sb);
+static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp);
+static void __dcache_remove_hash(cache_ent_t *bp);
+
+/*----------------------------------------------------------------------*/
+/* Static functions */
+/*----------------------------------------------------------------------*/
+static void push_to_mru(cache_ent_t *bp, cache_ent_t *list)
+{
+ bp->next = list->next;
+ bp->prev = list;
+ list->next->prev = bp;
+ list->next = bp;
+}
+
+static void push_to_lru(cache_ent_t *bp, cache_ent_t *list)
+{
+ bp->prev = list->prev;
+ bp->next = list;
+ list->prev->next = bp;
+ list->prev = bp;
+}
+
+static void move_to_mru(cache_ent_t *bp, cache_ent_t *list)
+{
+ bp->prev->next = bp->next;
+ bp->next->prev = bp->prev;
+ push_to_mru(bp, list);
+}
+
+static void move_to_lru(cache_ent_t *bp, cache_ent_t *list)
+{
+ bp->prev->next = bp->next;
+ bp->next->prev = bp->prev;
+ push_to_lru(bp, list);
+}
+
+static inline s32 __check_hash_valid(cache_ent_t *bp)
+{
+#ifdef DEBUG_HASH_LIST
+ if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
+ (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
+ return -EINVAL;
+ }
+#endif
+ if ((bp->hash.next == bp) || (bp->hash.prev == bp))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void __remove_from_hash(cache_ent_t *bp)
+{
+ (bp->hash.prev)->hash.next = bp->hash.next;
+ (bp->hash.next)->hash.prev = bp->hash.prev;
+ bp->hash.next = bp;
+ bp->hash.prev = bp;
+#ifdef DEBUG_HASH_LIST
+ bp->hash.next = (cache_ent_t *)DEBUG_HASH_NEXT;
+ bp->hash.prev = (cache_ent_t *)DEBUG_HASH_PREV;
+#endif
+}
+
+/* Do FAT mirroring (don't sync)
+ * sec: sector No. in FAT1
+ * bh: bh of sec.
+ */
+static inline s32 __fat_copy(struct super_block *sb, u64 sec, struct buffer_head *bh, int sync)
+{
+#ifdef CONFIG_SDFAT_FAT_MIRRORING
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ u64 sec2;
+
+ if (fsi->FAT2_start_sector != fsi->FAT1_start_sector) {
+ sec2 = sec - fsi->FAT1_start_sector + fsi->FAT2_start_sector;
+ BUG_ON(sec2 != (sec + (u64)fsi->num_FAT_sectors));
+
+ MMSG("BD: fat mirroring (%llu in FAT1, %llu in FAT2)\n", sec, sec2);
+ if (write_sect(sb, sec2, bh, sync))
+ return -EIO;
+ }
+#else
+ /* DO NOTHING */
+#endif
+ return 0;
+} /* end of __fat_copy */
+
+/*
+ * returns 1, if bp is flushed
+ * returns 0, if bp is not dirty
+ * returns -1, if error occurs
+ */
+static s32 __fcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync)
+{
+ if (!(bp->flag & DIRTYBIT))
+ return 0;
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ // Make buffer dirty (XXX: Naive impl.)
+ if (write_sect(sb, bp->sec, bp->bh, 0))
+ return -EIO;
+
+ if (__fat_copy(sb, bp->sec, bp->bh, 0))
+ return -EIO;
+#endif
+ bp->flag &= ~(DIRTYBIT);
+
+ if (sync)
+ sync_dirty_buffer(bp->bh);
+
+ return 1;
+}
+
+static s32 __fcache_ent_discard(struct super_block *sb, cache_ent_t *bp)
+{
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ __fcache_remove_hash(bp);
+ bp->sec = ~0;
+ bp->flag = 0;
+
+ if (bp->bh) {
+ __brelse(bp->bh);
+ bp->bh = NULL;
+ }
+ move_to_lru(bp, &fsi->fcache.lru_list);
+ return 0;
+}
+
+u8 *fcache_getblk(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ u32 page_ra_count = FCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits;
+
+ bp = __fcache_find(sb, sec);
+ if (bp) {
+ if (bdev_check_bdi_valid(sb)) {
+ __fcache_ent_flush(sb, bp, 0);
+ __fcache_ent_discard(sb, bp);
+ return NULL;
+ }
+ move_to_mru(bp, &fsi->fcache.lru_list);
+ return bp->bh->b_data;
+ }
+
+ bp = __fcache_get(sb);
+ if (!__check_hash_valid(bp))
+ __fcache_remove_hash(bp);
+
+ bp->sec = sec;
+ bp->flag = 0;
+ __fcache_insert_hash(sb, bp);
+
+ /* Naive FAT read-ahead (increase I/O unit to page_ra_count) */
+ if ((sec & (page_ra_count - 1)) == 0)
+ bdev_readahead(sb, sec, (u64)page_ra_count);
+
+ /*
+ * patch 1.2.4 : buffer_head null pointer exception problem.
+ *
+ * When read_sect is failed, fcache should be moved to
+ * EMPTY hash_list and the first of lru_list.
+ */
+ if (read_sect(sb, sec, &(bp->bh), 1)) {
+ __fcache_ent_discard(sb, bp);
+ return NULL;
+ }
+
+ return bp->bh->b_data;
+}
+
+static inline int __mark_delayed_dirty(struct super_block *sb, cache_ent_t *bp)
+{
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ if (fsi->vol_type == EXFAT)
+ return -ENOTSUPP;
+
+ bp->flag |= DIRTYBIT;
+ return 0;
+#else
+ return -ENOTSUPP;
+#endif
+}
+
+
+
+s32 fcache_modify(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+
+ bp = __fcache_find(sb, sec);
+ if (!bp) {
+ sdfat_fs_error(sb, "Can`t find fcache (sec 0x%016llx)", sec);
+ return -EIO;
+ }
+
+ if (!__mark_delayed_dirty(sb, bp))
+ return 0;
+
+ if (write_sect(sb, sec, bp->bh, 0))
+ return -EIO;
+
+ if (__fat_copy(sb, sec, bp->bh, 0))
+ return -EIO;
+
+ return 0;
+}
+
+/*======================================================================*/
+/* Cache Initialization Functions */
+/*======================================================================*/
+s32 meta_cache_init(struct super_block *sb)
+{
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ s32 i;
+
+ /* LRU list */
+ fsi->fcache.lru_list.next = &fsi->fcache.lru_list;
+ fsi->fcache.lru_list.prev = fsi->fcache.lru_list.next;
+
+ for (i = 0; i < FAT_CACHE_SIZE; i++) {
+ fsi->fcache.pool[i].sec = ~0;
+ fsi->fcache.pool[i].flag = 0;
+ fsi->fcache.pool[i].bh = NULL;
+ fsi->fcache.pool[i].prev = NULL;
+ fsi->fcache.pool[i].next = NULL;
+ push_to_mru(&(fsi->fcache.pool[i]), &fsi->fcache.lru_list);
+ }
+
+ fsi->dcache.lru_list.next = &fsi->dcache.lru_list;
+ fsi->dcache.lru_list.prev = fsi->dcache.lru_list.next;
+ fsi->dcache.keep_list.next = &fsi->dcache.keep_list;
+ fsi->dcache.keep_list.prev = fsi->dcache.keep_list.next;
+
+ // Initially, all the BUF_CACHEs are in the LRU list
+ for (i = 0; i < BUF_CACHE_SIZE; i++) {
+ fsi->dcache.pool[i].sec = ~0;
+ fsi->dcache.pool[i].flag = 0;
+ fsi->dcache.pool[i].bh = NULL;
+ fsi->dcache.pool[i].prev = NULL;
+ fsi->dcache.pool[i].next = NULL;
+ push_to_mru(&(fsi->dcache.pool[i]), &fsi->dcache.lru_list);
+ }
+
+ /* HASH list */
+ for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) {
+ fsi->fcache.hash_list[i].sec = ~0;
+ fsi->fcache.hash_list[i].hash.next = &(fsi->fcache.hash_list[i]);
+;
+ fsi->fcache.hash_list[i].hash.prev = fsi->fcache.hash_list[i].hash.next;
+ }
+
+ for (i = 0; i < FAT_CACHE_SIZE; i++)
+ __fcache_insert_hash(sb, &(fsi->fcache.pool[i]));
+
+ for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) {
+ fsi->dcache.hash_list[i].sec = ~0;
+ fsi->dcache.hash_list[i].hash.next = &(fsi->dcache.hash_list[i]);
+
+ fsi->dcache.hash_list[i].hash.prev = fsi->dcache.hash_list[i].hash.next;
+ }
+
+ for (i = 0; i < BUF_CACHE_SIZE; i++)
+ __dcache_insert_hash(sb, &(fsi->dcache.pool[i]));
+
+ return 0;
+}
+
+s32 meta_cache_shutdown(struct super_block *sb)
+{
+ return 0;
+}
+
+/*======================================================================*/
+/* FAT Read/Write Functions */
+/*======================================================================*/
+s32 fcache_release_all(struct super_block *sb)
+{
+ s32 ret = 0;
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ s32 dirtycnt = 0;
+
+ bp = fsi->fcache.lru_list.next;
+ while (bp != &fsi->fcache.lru_list) {
+ s32 ret_tmp = __fcache_ent_flush(sb, bp, 0);
+
+ if (ret_tmp < 0)
+ ret = ret_tmp;
+ else
+ dirtycnt += ret_tmp;
+
+ bp->sec = ~0;
+ bp->flag = 0;
+
+ if (bp->bh) {
+ __brelse(bp->bh);
+ bp->bh = NULL;
+ }
+ bp = bp->next;
+ }
+
+ DMSG("BD:Release / dirty fat cache: %d (err:%d)\n", dirtycnt, ret);
+ return ret;
+}
+
+
+/* internal DIRTYBIT marked => bh dirty */
+s32 fcache_flush(struct super_block *sb, u32 sync)
+{
+ s32 ret = 0;
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ s32 dirtycnt = 0;
+
+ bp = fsi->fcache.lru_list.next;
+ while (bp != &fsi->fcache.lru_list) {
+ ret = __fcache_ent_flush(sb, bp, sync);
+ if (ret < 0)
+ break;
+
+ dirtycnt += ret;
+ bp = bp->next;
+ }
+
+ MMSG("BD: flush / dirty fat cache: %d (err:%d)\n", dirtycnt, ret);
+ return ret;
+}
+
+static cache_ent_t *__fcache_find(struct super_block *sb, u64 sec)
+{
+ s32 off;
+ cache_ent_t *bp, *hp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ off = (sec + (sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE - 1);
+ hp = &(fsi->fcache.hash_list[off]);
+ for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) {
+ if (bp->sec == sec) {
+ /*
+ * patch 1.2.4 : for debugging
+ */
+ WARN(!bp->bh, "[SDFAT] fcache has no bh. "
+ "It will make system panic.\n");
+
+ touch_buffer(bp->bh);
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+static cache_ent_t *__fcache_get(struct super_block *sb)
+{
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ bp = fsi->fcache.lru_list.prev;
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ while (bp->flag & DIRTYBIT) {
+ cache_ent_t *bp_prev = bp->prev;
+
+ bp = bp_prev;
+ if (bp == &fsi->fcache.lru_list) {
+ DMSG("BD: fat cache flooding\n");
+ fcache_flush(sb, 0); // flush all dirty FAT caches
+ bp = fsi->fcache.lru_list.prev;
+ }
+ }
+#endif
+// if (bp->flag & DIRTYBIT)
+// sync_dirty_buffer(bp->bh);
+
+ move_to_mru(bp, &fsi->fcache.lru_list);
+ return bp;
+}
+
+static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp)
+{
+ s32 off;
+ cache_ent_t *hp;
+ FS_INFO_T *fsi;
+
+ fsi = &(SDFAT_SB(sb)->fsi);
+ off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE-1);
+
+ hp = &(fsi->fcache.hash_list[off]);
+ bp->hash.next = hp->hash.next;
+ bp->hash.prev = hp;
+ hp->hash.next->hash.prev = bp;
+ hp->hash.next = bp;
+}
+
+
+static void __fcache_remove_hash(cache_ent_t *bp)
+{
+#ifdef DEBUG_HASH_LIST
+ if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
+ (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
+ EMSG("%s: FATAL: tried to remove already-removed-cache-entry"
+ "(bp:%p)\n", __func__, bp);
+ return;
+ }
+#endif
+ WARN_ON(bp->flag & DIRTYBIT);
+ __remove_from_hash(bp);
+}
+
+/*======================================================================*/
+/* Buffer Read/Write Functions */
+/*======================================================================*/
+/* Read-ahead a cluster */
+s32 dcache_readahead(struct super_block *sb, u64 sec)
+{
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ struct buffer_head *bh;
+ u32 max_ra_count = DCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits;
+ u32 page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits;
+ u32 adj_ra_count = max(fsi->sect_per_clus, page_ra_count);
+ u32 ra_count = min(adj_ra_count, max_ra_count);
+
+ /* Read-ahead is not required */
+ if (fsi->sect_per_clus == 1)
+ return 0;
+
+ if (sec < fsi->data_start_sector) {
+ EMSG("BD: %s: requested sector is invalid(sect:%llu, root:%llu)\n",
+ __func__, sec, fsi->data_start_sector);
+ return -EIO;
+ }
+
+ /* Not sector aligned with ra_count, resize ra_count to page size */
+ if ((sec - fsi->data_start_sector) & (ra_count - 1))
+ ra_count = page_ra_count;
+
+ bh = sb_find_get_block(sb, sec);
+ if (!bh || !buffer_uptodate(bh))
+ bdev_readahead(sb, sec, (u64)ra_count);
+
+ brelse(bh);
+
+ return 0;
+}
+
+/*
+ * returns 1, if bp is flushed
+ * returns 0, if bp is not dirty
+ * returns -1, if error occurs
+ */
+static s32 __dcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync)
+{
+ if (!(bp->flag & DIRTYBIT))
+ return 0;
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ // Make buffer dirty (XXX: Naive impl.)
+ if (write_sect(sb, bp->sec, bp->bh, 0))
+ return -EIO;
+#endif
+ bp->flag &= ~(DIRTYBIT);
+
+ if (sync)
+ sync_dirty_buffer(bp->bh);
+
+ return 1;
+}
+
+static s32 __dcache_ent_discard(struct super_block *sb, cache_ent_t *bp)
+{
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ MMSG("%s : bp[%p] (sec:%016llx flag:%08x bh:%p) list(prev:%p next:%p) "
+ "hash(prev:%p next:%p)\n", __func__,
+ bp, bp->sec, bp->flag, bp->bh, bp->prev, bp->next,
+ bp->hash.prev, bp->hash.next);
+
+ __dcache_remove_hash(bp);
+ bp->sec = ~0;
+ bp->flag = 0;
+
+ if (bp->bh) {
+ __brelse(bp->bh);
+ bp->bh = NULL;
+ }
+
+ move_to_lru(bp, &fsi->dcache.lru_list);
+ return 0;
+}
+
+u8 *dcache_getblk(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ bp = __dcache_find(sb, sec);
+ if (bp) {
+ if (bdev_check_bdi_valid(sb)) {
+ MMSG("%s: found cache(%p, sect:%llu). But invalid BDI\n"
+ , __func__, bp, sec);
+ __dcache_ent_flush(sb, bp, 0);
+ __dcache_ent_discard(sb, bp);
+ return NULL;
+ }
+
+ if (!(bp->flag & KEEPBIT)) // already in keep list
+ move_to_mru(bp, &fsi->dcache.lru_list);
+
+ return bp->bh->b_data;
+ }
+
+ bp = __dcache_get(sb);
+
+ if (!__check_hash_valid(bp))
+ __dcache_remove_hash(bp);
+
+ bp->sec = sec;
+ bp->flag = 0;
+ __dcache_insert_hash(sb, bp);
+
+ if (read_sect(sb, sec, &(bp->bh), 1)) {
+ __dcache_ent_discard(sb, bp);
+ return NULL;
+ }
+
+ return bp->bh->b_data;
+
+}
+
+s32 dcache_modify(struct super_block *sb, u64 sec)
+{
+ s32 ret = -EIO;
+ cache_ent_t *bp;
+
+ set_sb_dirty(sb);
+
+ bp = __dcache_find(sb, sec);
+ if (unlikely(!bp)) {
+ sdfat_fs_error(sb, "Can`t find dcache (sec 0x%016llx)", sec);
+ return -EIO;
+ }
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ if (SDFAT_SB(sb)->fsi.vol_type != EXFAT) {
+ bp->flag |= DIRTYBIT;
+ return 0;
+ }
+#endif
+ ret = write_sect(sb, sec, bp->bh, 0);
+
+ if (ret) {
+ DMSG("%s : failed to modify buffer(err:%d, sec:%llu, bp:0x%p)\n",
+ __func__, ret, sec, bp);
+ }
+
+ return ret;
+}
+
+s32 dcache_lock(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+
+ bp = __dcache_find(sb, sec);
+ if (likely(bp)) {
+ bp->flag |= LOCKBIT;
+ return 0;
+ }
+
+ EMSG("%s : failed to lock buffer(sec:%llu, bp:0x%p)\n", __func__, sec, bp);
+ return -EIO;
+}
+
+s32 dcache_unlock(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+
+ bp = __dcache_find(sb, sec);
+ if (likely(bp)) {
+ bp->flag &= ~(LOCKBIT);
+ return 0;
+ }
+
+ EMSG("%s : failed to unlock buffer (sec:%llu, bp:0x%p)\n", __func__, sec, bp);
+ return -EIO;
+}
+
+s32 dcache_release(struct super_block *sb, u64 sec)
+{
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ bp = __dcache_find(sb, sec);
+ if (unlikely(!bp))
+ return -ENOENT;
+
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ if (bp->flag & DIRTYBIT) {
+ if (write_sect(sb, bp->sec, bp->bh, 0))
+ return -EIO;
+ }
+#endif
+ bp->sec = ~0;
+ bp->flag = 0;
+
+ if (bp->bh) {
+ __brelse(bp->bh);
+ bp->bh = NULL;
+ }
+
+ move_to_lru(bp, &fsi->dcache.lru_list);
+ return 0;
+}
+
+s32 dcache_release_all(struct super_block *sb)
+{
+ s32 ret = 0;
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ s32 dirtycnt = 0;
+#endif
+
+ /* Connect list elements:
+ * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last)
+ */
+ while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) {
+ cache_ent_t *bp_keep = fsi->dcache.keep_list.prev;
+ // bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later
+ move_to_mru(bp_keep, &fsi->dcache.lru_list);
+ }
+
+ bp = fsi->dcache.lru_list.next;
+ while (bp != &fsi->dcache.lru_list) {
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ if (bp->flag & DIRTYBIT) {
+ dirtycnt++;
+ if (write_sect(sb, bp->sec, bp->bh, 0))
+ ret = -EIO;
+ }
+#endif
+ bp->sec = ~0;
+ bp->flag = 0;
+
+ if (bp->bh) {
+ __brelse(bp->bh);
+ bp->bh = NULL;
+ }
+ bp = bp->next;
+ }
+
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ DMSG("BD:Release / dirty buf cache: %d (err:%d)", dirtycnt, ret);
+#endif
+ return ret;
+}
+
+
+s32 dcache_flush(struct super_block *sb, u32 sync)
+{
+ s32 ret = 0;
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+ s32 dirtycnt = 0;
+ s32 keepcnt = 0;
+
+ /* Connect list elements:
+ * LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last)
+ */
+ while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) {
+ cache_ent_t *bp_keep = fsi->dcache.keep_list.prev;
+
+ bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later
+ move_to_mru(bp_keep, &fsi->dcache.lru_list);
+ keepcnt++;
+ }
+
+ bp = fsi->dcache.lru_list.next;
+ while (bp != &fsi->dcache.lru_list) {
+ if (bp->flag & DIRTYBIT) {
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ // Make buffer dirty (XXX: Naive impl.)
+ if (write_sect(sb, bp->sec, bp->bh, 0)) {
+ ret = -EIO;
+ break;
+ }
+
+#endif
+ bp->flag &= ~(DIRTYBIT);
+ dirtycnt++;
+
+ if (sync != 0)
+ sync_dirty_buffer(bp->bh);
+ }
+ bp = bp->next;
+ }
+
+ MMSG("BD: flush / dirty dentry cache: %d (%d from keeplist, err:%d)\n",
+ dirtycnt, keepcnt, ret);
+ return ret;
+}
+
+static cache_ent_t *__dcache_find(struct super_block *sb, u64 sec)
+{
+ s32 off;
+ cache_ent_t *bp, *hp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ off = (sec + (sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE - 1);
+
+ hp = &(fsi->dcache.hash_list[off]);
+ for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) {
+ if (bp->sec == sec) {
+ touch_buffer(bp->bh);
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+static cache_ent_t *__dcache_get(struct super_block *sb)
+{
+ cache_ent_t *bp;
+ FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
+
+ bp = fsi->dcache.lru_list.prev;
+#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
+ while (bp->flag & (DIRTYBIT | LOCKBIT)) {
+ cache_ent_t *bp_prev = bp->prev; // hold prev
+
+ if (bp->flag & DIRTYBIT) {
+ MMSG("BD: Buf cache => Keep list\n");
+ bp->flag |= KEEPBIT;
+ move_to_mru(bp, &fsi->dcache.keep_list);
+ }
+ bp = bp_prev;
+
+ /* If all dcaches are dirty */
+ if (bp == &fsi->dcache.lru_list) {
+ DMSG("BD: buf cache flooding\n");
+ dcache_flush(sb, 0);
+ bp = fsi->dcache.lru_list.prev;
+ }
+ }
+#else
+ while (bp->flag & LOCKBIT)
+ bp = bp->prev;
+#endif
+// if (bp->flag & DIRTYBIT)
+// sync_dirty_buffer(bp->bh);
+
+ move_to_mru(bp, &fsi->dcache.lru_list);
+ return bp;
+}
+
+static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp)
+{
+ s32 off;
+ cache_ent_t *hp;
+ FS_INFO_T *fsi;
+
+ fsi = &(SDFAT_SB(sb)->fsi);
+ off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE-1);
+
+ hp = &(fsi->dcache.hash_list[off]);
+ bp->hash.next = hp->hash.next;
+ bp->hash.prev = hp;
+ hp->hash.next->hash.prev = bp;
+ hp->hash.next = bp;
+}
+
+static void __dcache_remove_hash(cache_ent_t *bp)
+{
+#ifdef DEBUG_HASH_LIST
+ if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
+ (bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
+ EMSG("%s: FATAL: tried to remove already-removed-cache-entry"
+ "(bp:%p)\n", __func__, bp);
+ return;
+ }
+#endif
+ WARN_ON(bp->flag & DIRTYBIT);
+ __remove_from_hash(bp);
+}
+
+
+/* end of cache.c */